How Code Structure Influences Culture and Work Habits / by Chris Shaffer

Much as a nation’s constitution and structure of its public institutions shapes its governing philosophy and society, the structure of your company’s code base will play some part in shaping your team’s culture, how functional groups work together, and how they approach problems.

It’s important to keep this in mind, as software architecture is not a purely technical exercise; it’s also an exercise in managing people.


Let’s start with two (oversimplified) examples:

Siloed Architecture

Consolidated Architecture

The Siloed Architecture Implies

  • Product teams are organized around subject matter expertise (i.e. people knowledgeable in credit markets, equity markets, and real estate markets as separate specializations)

  • Technologists need to be more “full stack”

  • Cross-team collaboration is rare

  • A user experience which is distinct, depending on what data set is being accessed

  • The natural approach to problem solving or idea generation will be “what does this sort of client want?”

The Consolidated Architecture Implies

  • Product teams are organized around what the product does - design and interfaces

  • The role of subject matter experts is to standardize and explain data to a team which must be familiar with all subject areas

  • Technologists may be more specialized (e.g. there is only point of responsibility for a charting library or search database, rather than three)

  • Cross-team collaboration is common

  • A user experience which is unified, even when different data sets are being accessed

  • The natural approach to problem solving or idea generation will be “what do these sorts of clients have in common?”

Caveats

Note the phrasing: “implies” - nothing here is an inviolable natural law. As with a constitution, we’re talking about a statement of values and scheme of organization that makes certain behaviors more natural than others.

A country’s constitution may grant the right of press freedom, but in practice the government may have journalists arrested on trumped-up tax charges… the constitution doesn’t serve as a magic bullet, but it’s a tool to make that much less likely.

Likewise, the Siloed Architecture example doesn’t absolutely prevent the credit team from developing an API that allows the equity team to include credit data in that product, or a cross-functional team from being formed to write CSS that all 3 products import for a uniform look and feel, but it does introduce a barrier to doing so. The Consolidated Architecture doesn’t absolutely require the charting team to be familiar with all three data sets, but it biases the organization in that direction.

Which one is right?

Short answer: it depends.

I would typically recommend a Consolidated Architecture for smaller or mid-sized companies focused on one thing. A Siloed Architecture becomes more appropriate as a company starts to look more like a constellation of services.

To reduce each of these to an absurd extreme:

  • It would add a lot of unnecessary complexity to create a marketplace software with separate “sellers” and “buyers” databases (even if you did want separate user interfaces) - inventory available to sell and inventory available to purchase are the same thing. Keeping these in separate stores of truth would introduce synchronization issues that would have to be solved; storing them together would mean those problems never existed.

  • There would be few, if any, people on Earth with the breadth of knowledge to work at a cloud hosting service offering virtual machines, networking, storage, databases, and dozens of other tools … if that company were structured in such a way that each engineer would be expected to contribute code to the internals of each of those tools. Having to understand how each of those services worked in too much detail would be an unnecessary burden.


This is also true for git branching models and release schedules

If you have a good CI/CD system, whether you’re “releasing a new version” daily or annually is immaterial, from technology perspective. There’s no “real” difference between a “hotfix” and a “major release.” These distinctions are arbitrary, and that’s okay!

Your branching model and release schedule are organizing principles. They provide structure and keep you sane. Of course, we could deploy any branch into production whenever we want or have bourbon for breakfast, but we’re not going to. It’s important to have them and stick to them ~~most of the time~~, though they needn’t be straitjackets.


A word on “microservices”

Let’s start with Wikipedia:

In a microservices architecture, services are fine-grained and the protocols are lightweight. The goal is that teams can bring their services to life independent of others. Loose coupling reduces all types of dependencies and the complexities around it, as service developers do not need to care about the users of the service, they do not force their changes onto users of the service. Therefore it allows organizations developing software to grow fast…

Interfaces need to be designed carefully and treated as a public API. Techniques like having multiple interfaces on the same service, or multiple versions of the same service, to not break existing users code.

A good microservice architecture may look like: a company which sells a dozen products to a dozen groups of customers, sometimes but not always overlapping. Each service’s code is stored in a separate repository, and they may or may not share a database. The services don’t rely too much on one another, and the architecture is treated as much as an org chart as anything else - ensuring each team is responsible for keeping its customers happy.

It may also look like: a small company that wants to abstract away multiple data storage systems behind a single interface. Each “service” may be as simple as a class. The front-end developers don’t need to know whether a particular search API uses a fulltext search database, a graph database, or dynamic SQL (or a combination of the three, depending on parameters). This may have just been called “loose coupling” or “modularity” in the old days.

Some teams think that this means “a repository or three for every developer”. It doesn’t.

A lot of teams think the last two sentences refer simply to not breaking the compilation or build process - an update to microservice A can rely on a new schema, but as long as microservice B can point to the old API (which in turn points to an old database that is no longer updated), microservice B won’t crash. That’s a generous interpretation of “not breaking”.

With a bad microservice architecture, a team is taking this section from the classic Mythical Man Month and sweeping roughly two-thirds of their task under a rug.

[A program] is complete in itself, ready to be run by the author on the system on which it was developed. That is the thing commonly produced in garages …

… a program becomes a component in a programming system. This is a collection of interacting programs, coordinated in function …

It is time-consuming for subtle bugs arise from unexpected interactions of debugged components. A programming systems component costs at least three times as much as a stand-alone program of the same function.

(emphasis mine)

Some aspects of software design haven’t changed much in 50 years. Decoupling things that don’t need to be coupled is usually good design. Attaching those decoupled components back together is additional work, on top of building them in the first place. The tension between these must be navigated - the best approaches tend to involve some variation on:

  1. building trivial components with a plan in mind to link them into a coherent product

  2. linking those components together to get an end-to-end coherent if trivial product

  3. expanding the functionality of each component to make your product useful


Conclusions

As usual, there are few simple and absolute right answers. If software were easy, more people would do it and it wouldn’t be so lucrative.

A few rules of thumb:

  • If you have the luxury of planning an architecture and an org chart from scratch, do those together - one will impact the other (or, if they don’t, you’ll have friction later)

  • Where a team and an architecture is already in place, consider whether a re-architecture might better serve the team dynamic in addition to other considerations (or vice versa, consider whether a change to the org chart can improve how people work within the guardrails your architecture has created)

  • These decisions matter and should be made thoughtfully, but they’re neither permanent nor absolute - don’t obsess too much over them

  • The “right” architecture and org chart often will change as an organization evolves - you may start with a consolidated architecture and later, via mergers, find that it’s one of five microservices each belonging to a (acquired) business unit

  • If an architecture seems to be intrinsically tied to poor personnel management, it’s probably not appropriate - an architecture with 10 microservices, each with its own technology stack, may be sensible for a team of 200 and simultaneously a poor choice for a team of 5 - you would never put half of a person into a box on your org chart … would you?