The Engineer’s Dilemma

Adam Parrish
Neosavvy Labs
Published in
4 min readMar 1, 2017

Introducing Complexity and Building Useful Abstractions

No matter how well architected the code is, when you inherit a large body of work from a previous engineering team there is no question that you are standing at the bottom of a giant mountain of domain knowledge and decisions made under the duress of a tight schedule.

It’s the natural inclination of any software developer to want to toss all the previous work by the past team, but the customer, stakeholder or product owner will suffer and that’s absolutely the wrong thing to do. A novice engineer will start over but a senior engineering team will find a way to stand up a pristine location to contribute new changes in a contained environment that secludes the now legacy code to its pit of death and deprecation. Over time best practices will be set and the team can migrate the old haphazard code to the new pristine location.

The key to remember here is that all code has a lifespan and will eventually always become someone else’s perception of legacy decision making. We have to remind ourselves to remain pragmatic and contribute product changes in a simple form that other future developers can understand and maintain. Time spent introducing abstractions and complexity are only valuable if left behind with a high level of understanding and documentation for the remainder of the team. I’d argue that for every day spent creating a new abstraction, five days should be spent teaching others how it works and how to use it. Otherwise what was the use of creating it?

The above graph shows my opinion on introducing abstractions. Their usefulness steeply declines as the complexity of the abstraction grows towards a maximum of complexity. Arguably 80% of the usefulness is achieved in the first 10–20% of the complexity that is added. There are rare cases where extra complexity is worthwhile, but in the typical customer implementation phase of a project it is more worthwhile to focus on simplicity.

Introducing a lot of documentation and pattern based examples is a great way to communicate a new internal approach to solving a problem in code. Boilerplate is also a useful approach albeit boring to write it comes with a number of benefits:

  1. You chose a framework with documentation and the team who provides it will continually update and provide more
  2. Anyone from outside your team can contribute to your code base without investing time in learning your internal abstraction framework

Negatives of building huge internal abstraction layers

  1. If you build on top of redux and redux shifts against your abstractions you are going to be forced to deal with the repercussions which could include staying where you are on the version of the library, or rewriting multiple large modules of functionality in your code that the product team already is happy with.
  2. You may miss out on the communities solution to a problem you have solved leaving you and your team in a situation where you are supporting a solution that no one in the rest of the development world you live in has to deal with.

Needless to say sometimes we adopt frameworks that entirely get deprecated in favor of a new framework (reflux vs redux for example). At the time a team may choose to stay with their choice made based on the best practices of the time, or they can absorb the rewrite or migration. I argue that a migration from any framework to another is a waste of time in most cases. Chances are the product will suffer, I regularly remind myself of the age old tale of browser functionality back in the days of Netscape and Internet Explorer. I believe the tl;dr; is that Netscape spent 3 years re-architecting and wasting precious product cycles eventually losing so much market share to I.E. that it killed the browser completely. Joel Spolsky referred to this as ‘the single worst strategic mistake’.

“They did it by making the single worst strategic mistake that any software company can make:

They decided to rewrite the code from scratch.”

We can learn from this and ask ourselves as engineers what we can do for our users? Is the creation of this framework helpful, from my users perspective? Will I be able to introduce new features faster for my users? Will more engineers be more proficient?

As engineers we constantly want to improve our lives in our code, but we need to remind ourselves why we do this; we are here to deliver product to our customers for our product owners.

--

--