We define Waku as modular. In software engineering, modularity is connoted with good. It usually means good separation of concerns, flexibility in selecting just what you need, well-defined interfaces, and portability.
This aligns with Waku’s concept of adaptive nodes. Enabling application developers building on Waku to set it up just right for their users’ needs and resources.
Danger of Choice
The flip side of modularity and flexibility is the ability, and obligation, to choose. What modules (protocols) should I be using for my application? What if my application runs on mobile? What about a node to run in a VPS?
Every choice a developer must make when using a technology, is a potential point of friction and mistake. It is one more question they have to ask themselves and consider while building.
One more opportunity to give up and use a simpler, easier-to-use, alternative.
Modularity should be an asset, not a liability.
Good Defaults - Not So Good
The concept of good defaults is a good solution to this problem. Enabling flexibility to the developers, while providing a turnkey solution. The developers have choice, but do not have to make them to get started.
While I am a strong believer of a good defaults approach, there is a hidden cost to it: design a flexible API.
By exposing every potential decision to the developers: from parameter values (e.g. timeout) to full behavior (e.g. whether to enable this or this discovery method), we have to build extensible and flexible APIs. This costs time and effort, not only to design but also to maintain. More complex the API, harder is it to evolve it without breaking backward compatibility. Every breaking change needs to be documented and with a roll out strategy, further increasing maintenance and development costs.
Modularity and single-purpose can be used here, but it usually mean for the developer to juggle with various single-purpose artifacts, that they may not care about. One can think of a very popular library in our domain as a stellar example.
Applying the 80-20 Rule
80 percent of all end users generally use only 20 percent of a software application’s features
It is a common adage in the software industry.
It also applies to library and API where users are developers.
What does it mean concretely? It means being very deliberate about what is configurable and what is not. It means going beyond the good default principle, by simply removing choice.
Working software over flexible API
In the case of Waku, we spend the past few months fine-tuning Waku usage in the Status desktop and mobile app to improve connectivity and reliability. There is a wide array of potential choices and parameters. Learning from the Status application, we have also made some choices that were not obvious at first. Such as using peer exchange alongside discv5 on desktop.
While flexibility might have some value, we should not be handing footguns to developers.
Finally, we need to remember that all our code is open source. The 20% developers that need more flexibility can always fork on open pull requests.
So What does “Modular” Mean for Waku?
Waku is modular.
Yet, it must not mean that a developer has to make a million choices to use Waku.
Yet, it should not mean 20+ lines of code to get started,
or 200+ lines of docs to configure Waku.
Waku’s codebase should be modular, applying separation of concern across function modules to enable maintainability, flexibility, and orthogonal testing.
The adaptive node concept still applies. Because Waku needs to work on all platforms, with various resources. However, it should not mean each developer must decide what combination of protocols (out of 10+) they should select to get it working. Instead, we, the Waku team, must provide limited choices, based on our learnings.
For example, providing 2 modes of operation: relay or edge.
- Only allowing edge in browser and mobile
- Allowing relay and edge on desktop native, defaulting to edge
- etc
More choices can lead to footgun, that can impact reliability.
What Should the API Look Like?
This is what we should strive for.
The verbs proposed in the reliability article are truly meant to drive Waku’s API design.
We need to understand the minimal information needed for an application, and deduce the rest.
For example
const contentTopic = createContentTopic("my app") // We recommend applications to start with a single content topic, and move to several topics as they scale
const node = createNode(contentTopic)
// Create a node
// Uses TWN cluster, and autosharding by default with RLNaaS (for now)
// Operation mode edge, or relay, selected based on the platform
// Protocols (relay, discv5, etc) deduced from mode and enabled.
const payload = ... // The one thing a developer cares about
const sending = node.send(payload)
// uses relay or lightpush depending on the mode, the developer does not care
sending.addEventListener('sent', {
// Usage of generic terminology across
// this may mean "light push ack" or "sent over relay and we believe we had good connectivity", deduced from regular ping to connected relay peers
}
sending.addEventListener('networkConfirmed', {
// store confirmation strategy is applied by default
}
const subscription = node.subscribe(contentTopic)
subscription.addEventListener('message'), {
// Recovery store queries are automatically performed on any subscription
// So is peer management, redundant filter subscriptions and filter pings
}
Conclusion
We have learned from Status and other projects that reliability out of the box is expected. Developers should be able to focus on their application logic, not how to make Waku work for their application.
By dogfooding Waku in the Status applications, we are acquiring valuable knowledge on the best way to use Waku. Let’s deliver this knowledge to all projects by having simple, limited APIs, which are powerful by their reliability and ease to use.
The good default principles still apply, and it does so strongly. The expectation is that close to no parameters should be passed by status-go when calling on Golang SDK, as what works for Status should be Waku’s default behaviour and work for other.
As we move forward with 2025 H1 planning, I expect us to spend more time defining what it means and have this simplicity reflected across our current SDKs (Golang, Nim, JS) and potentially new ones.