The Chicken or the Egg: Sequencing in Software Development Part I

Jon Behnken
February 20, 2025

In his book, Clean Architecture, Robert C. Martin says that the “behavior [of software] is urgent but not always particularly important [and the] architecture is important but never particularly urgent.” Although it took me some time to understand what he meant by that, eventually I recognized it as an elegant way of framing a fundamental dilemma in software development: Should a system’s features and functionality dictate its architecture, or should architectural decisions inform how the underlying features are implemented?

At Nayya, we lean toward the latter. We believe that great software originates from well-designed architecture. In order to design a system that effectively emulates the behaviors of things in the real world, we need to first understand how they communicate. This perspective lends itself well to prioritizing architectural decisions first and then letting application development follow and to shape an organizational structure that makes the most sense for our domain. Alongside our high-level system design, we also focused on creating a knowledge-rich data model. Out of this loose structure formed by both our architectural and data model decisions, the behavior of the underlying applications emerge almost naturally as a consequence of both the responsibility of their parent domain and their placement within the organizational hierarchy. This philosophy ultimately guided our transition to an event-driven, primarily serverless system.

Decentralization vs. Consistency: The Balancing Act

Event-based systems offer scalability and flexibility but they also introduce new challenges – most notably, consistency amongst distributed responsibility.

 

Source

Each domain in our system is responsible for maintaining its own subset of objects that together comprise our unified data model. However, because artifacts often span multiple domains, requiring each domain to internally manage its own data could jeopardize system-wide consistency if critical operational instructions from an external client fail to process. For example, if some internal process in the Support context necessitates the deletion of a Product, it must communicate that to the Sales team (see the diagram above). Any failure in message transport, delivery or processing, could lead to inconsistencies between the two domains.

In the realm of employee benefits, one of our most critical requirements is determining eligibility. To build a health and wealth platform that delivers meaningful, transformative impact, accuracy must be our first priority. Democratizing access to opportunities that are ultimately incompatible with our users would be fundamentally detrimental to our mission here at Nayya. At the core of the concept of eligibility is consistency – if one domain in our ecosystem makes an eligibility determination based on a set of rules and attributes, another service within our organization must reach the same conclusion when using the same criteria. 

If we were to embrace the feature-forward philosophy of development, we’d simply create a centralized eligibility service to achieve this level of consistency. However a consolidated eligibility service would, among other things, introduce a single point of failure into our distributed system, disrupt the autonomy of each domain and undermine the precept of independence that is central to our design. We believe that each application in our system is the best possible decision-maker with respect to the line of business they represent. For instance, the eligibility rules and necessary data to make determinations for benefit selection and enrollment are different from those for the utilization of existing benefits. Those use cases represent two different applications and though they share a dependency in the form of a Benefit object, they should be able to evaluate eligibility independently while also assuming the other domain will generate the same output given the same conditions.

If you’re interested in learning how Nayya approached and ultimately solved this problem, be sure to check out the follow-on blog post when it becomes available!