Tool vs Framework practice

In line with my results vs system thinking exploration, I think I’ve found what may be another dichotomy in software development practice. The tool over framework principle has been fundamental for my engineering practice for my whole professional career. Composable tools have served me better than large frameworks. Usually I’ve considered it a style choice with consequences, but it may represent a more fundamental attitude towards engineering. Even if your system doesn’t use an explicit 3rd party software framework, the framework way of thinking may be present. As always, this is not a black & white descriptor.

Tools

  • Unix principle: do one thing and do it well
  • Composable tools provide platform as needed.
  • Enable and empower.
  • Give developers tools to build with.
  • Intimately familiar with tools.
  • Likes new tools, actively adopts them.
  • Likes the command line.
  • Learns by reading source and documentation.
  • Vertical development.

Benefits

  • Flexibility.
  • Reusable and composible functionality. Encapsulation.
  • Application performance from choosing only the tools needed.
  • Adoption of new tools rather than building.
  • Reduce costs by leveraging the best tools.

Costs

  • Upfront investment in learning multiple tools
  • Fragmentation of implementation.
  • Emergent behavior between tools and components.
  • Complexity of system.
  • Fragility

Frameworks

  • Microsoft principle: play in our garden, it’s got everything you could need
  • convention over configuration.
  • Insulate and focus.
  • Give developers places to slot in logic. Frameworks provide all platform details.
  • Likes IDEs.
  • Always wants familiar tools. Distrusts new tools or new uses of tools.
  • Learn tools to the level that the benefit is achieved, no further.
  • Learns by example and imitation. Likes recipes and copy/paste.
  • Web frameworks as ur-example.
  • Horizontal development.

Benefits

  • Knowledge transfer between organizations. (“Looking for a Rails developer…”)
  • Quick turnaround from idea to something presentable.
  • Commercial tools and support.
  • Reduce costs by focusing on business logic over implementation details.

Costs

  • Founders once framework no longer provides functionality.
  • Sprawl and inertia.
  • Reiventing the wheel, ineffective tool use.
  • Duplication and redundancy.

Horizontal vs vertical

Tool and framework nicely describe an attitude a developer or team has towards their work. There are two descriptors of the software projects themselves I’ve used for a while, which I think very closely map to the tool and framework thinking: I call them vertical and horizontal.

A vertical system has narrowly focused independent components, building up complexity through the combination, usually in a heirarchy. Vertical systems leverage tools appropriate at each component.

Horizontal systems are very flat, with many similar broad components at the same level. Horizontal systems leverage frameworks and use the same set of tools for each component.

The classic example of a horizontal system is one using the typical web framework (Rails, Django, Cake, etc). Ostensibly there are layers (a distinct pattern “Rails-MVC”), but it usually devolves into what I call macaroni code. Each component has a data access system bound to one database table, a routing class, and a UI template. Repeat for every table in your database. Best case, you get these macaroni tubes of functionality from the UI to the datastore. Horizontal development scales well. New functionality can be added without affecting architecture. There is a strong established pattern for devs to follow; it lends itself well to most dev shops and outsourcing firms where developers are mostly low skilled. Functionality is (hopefully) strongly grouped and easy to find. Service oriented architecture to this style means splitting the macaroni into separate processes. Failure modes include; needs beyond the basics of the horizontal layer, problems outside the scope of the framework, unmanageable sprawl, slow moving change. Compare with an assembly line - lots of very similar operations done at a large scale.

Vertical systems are more rare, and less visible due to their general lack of reliance on frameworks. Growth of non-traditional software tools in data analysis, distributed systems, and embedded systems have driven tool development. In the web space, tools like Sinatra and akka-http represent a different approach, providing only routing functionality and expecting other tools to provide for other needs. Akka itself is a great example of providing a set of tools for solving concurrency model problems. Vertical systems are more likely to become spaghetti and fragile, due to the increasing complexity of interconnected components. If each component becomes connected to most of the others, the number of possible connections goes up exponentially. Good vertical systems are build heirarchically, with the complexity managed by abstracting away details of a component’s inner workings and subcomponents. Service oriented architecture to this style is just another way to compose the system components together. Compare with an internal combustion engine - distinct components, working together in a complex way.

Results vs System oriented thinking

I spent quite some time thinking why I don’t understand the development practice of most programmers I’ve encountered. I realized there is a major gap in thinking between us. I’ve formulated this as two distinct camps - result oriented and system oriented. Obviously no organization or person will fit squarely in one camp, but I have found it a useful distinction.

Results oriented

Runtime behavior is your business product. Code is a means to an end.

Key indicator: someone would buy your company for your client base.

Concerned with generating income.

Now oriented.

Typical patterns

  • UI first.
  • Heavy reliance on frameworks (fallback to website always).
  • Use familiar tools beyond original purpose
  • First thing that compiles.
  • Customer driven. Very reactive.
  • Test driven development (result first).
  • Shortest path from point A to point B

Interests

  • Short term growth
  • Quick, reactive development
  • Technical debt is acceptable
  • Simpler problems solvable with a tool.

Failure modes

  • Technical debt outpacing growth
  • Intractable problems - e.g. not solvable in short time, requires new techniques, not suitable for current tools.
  • Over promising features

System oriented

Code is the product. Runtime behavior is a result of the product working

Key indicator: someone would buy your company for your technology.

Concerned with building capital.

Future oriented.

Typical patterns

  • System and data first.
  • Architecture driven.
  • Stakeholder driven.
  • Planned features.
  • Builds tools.

Interests

  • Sustainable product.
  • Patents and Licensing.
  • Long term stability.
  • Harder problems requiring new technology

Failure modes

  • Perfectionism
  • Failure to deliver
  • Over engineering (you ain’t gonna need it).
  • Unresponsive to customer needs.

I think the split represents what I’m seeing in most software shops - they are pretty much solely focused on results rather than systems. The first code that gets the results they want is the correct solution and then move on. I’ve watched devs with years of experience write brand new code which contains functions in the thousands of lines. They were unable to describe the functioning interactions in their code that they had written in the previous month. The code was not a system; but rather a large growing collection of functionality drifting as needed.

I’m very clearly in the system oriented thinking camp. I like to say that if you do your job right, no one will notice, which is probably true. The result oriented person appears to be much more productive since they are in the thick of reacting to problems and immediate needs. They also move quickly, because future consequences are not at the forefront of their thinking.

Proactive maintenance is very system oriented. Reactive changes to failures or criticism represent result driven thinking. If something isn’t actively an emergency it gets ignored. When you find a major problem, they quickly became interested in fixing the problem because the result was wrong. If they don’t see the incorrect result, it’s not a problem.

I think that much of my struggles have come from being at odds with other people’s goals. If I’m pushing for a longer dev time on a project to add unit tests or do some refactoring, I’m adding more work, not reducing it. The result is already done, why make it harder for yourself? I haven’t figured out the best way to align long term pain with good practice and consequences with current behavior.

On the flip side, I have struggled against the failure modes of the system oriented camp. Over engineering in particular is a struggle. My deep investment in backend and infrastructure over user interfaces and the whims of customers has frustrated people, making them think that I have no interest in their needs.