The Simple Solow Model of Software Engineering

Op­tional back­ground: The Su­per-Sim­ple Solow Model

Soft­ware is eco­nomic cap­i­tal—just like build­ings, in­fras­truc­ture, ma­chines, etc. It’s cre­ated once, and then used for a (rel­a­tively) long time. Us­ing it does not de­stroy it. Some­one who buys/​cre­ates a ma­chine usu­ally plans to use it to build other things and make back their in­vest­ment over time. Some­one who buys/​cre­ates soft­ware usu­ally plans to use it for other things and make back their in­vest­ment over time.

Soft­ware de­pre­ci­ates. Hard­ware needs to be re­placed (or cloud provider switched), op­er­at­ing sys­tems need to be up­graded, and back­ward com­pat­i­bil­ity is not always main­tained. Se­cu­rity prob­lems pop up, and need to be patched. Ex­ter­nal libraries are de­p­re­cated, aban­doned, and stop work­ing al­to­gether. Peo­ple shift from desk­top to browser to mo­bile to ???. Per­haps most fre­quently, ex­ter­nal APIs change for­mat or mean­ing or are shut down al­to­gether.

In most macroe­co­nomic mod­els, new cap­i­tal ac­cu­mu­lates un­til it reaches an equil­ibrium level, where all in­vest­ment goes to­ward re­pairing/​re­plac­ing de­pre­ci­ated cap­i­tal—re­sur­fac­ing roads, re­plac­ing ma­chines, re­pairing build­ings rather than cre­at­ing new roads, ma­chines and build­ings. The same ap­plies to soft­ware teams/​com­pa­nies: code ac­cu­mu­lates un­til it reaches an equil­ibrium level, where all effort goes to­ward re­pairing/​re­plac­ing de­pre­ci­ated code—switch­ing to new libraries, up­dat­ing to match changed APIs, and patch­ing bugs in­tro­duced by pre­vi­ous re­pairs.

What qual­i­ta­tive pre­dic­tions does this model make?

Pre­dic­tion 1

If a soft­ware com­pany wants to ex­pand the ca­pa­bil­ities of their soft­ware over time, they can’t just write more code—the old soft­ware will break down if the en­g­ineers turn their at­ten­tion el­se­where. That leaves a few op­tions:

  • Hire more en­g­ineers (eco­nomics equiv­a­lent: pop­u­la­tion/​la­bor force growth)

  • Hire/​train bet­ter en­g­ineers (eco­nomics equiv­a­lent: more ed­u­ca­tion)

  • Figure out bet­ter ways to make the soft­ware do what it does (eco­nomics equiv­a­lent: in­no­va­tion)

Hiring more en­g­ineers is the “throw money at it solu­tion”, and prob­a­bly the most com­mon in prac­tice—but also the solu­tion most prone to to­tal failure when the VC fund­ing dries up.

Hiring/​train­ing bet­ter en­g­ineers is the dream. Every soft­ware com­pany wishes they could do so, many even claim to do so, but few (if any) ac­tu­ally man­age it. There are many rea­sons: it’s hard to rec­og­nize skill lev­els above your own, ed­u­ca­tion is slow and hard to mea­sure, there’s lots of bul­lshit on the sub­ject and it’s hard to comb through, hard to get buy-in from man­age­ment, etc.

Figur­ing out bet­ter ways to make the soft­ware do what it does is prob­a­bly the most tech­ni­cally in­ter­est­ing item on the list, and also ar­guably the item with the most long-term po­ten­tial. This in­cludes adopt­ing new libraries/​lan­guages/​frame­works/​tech­niques. It in­cludes re­fac­tor­ing to unify du­pli­cate func­tion­al­ity. It in­cludes de­sign­ing new ab­strac­tion lay­ers. Un­for­tu­nately, all of these things are also easy to get wrong—unify­ing things with sig­nifi­cantly di­ver­gent use cases, or de­sign­ing a leaky ab­strac­tion—and it’s of­ten hard to tell un­til later whether the change has helped or hurt.

Pre­dic­tion 2

New prod­ucts from small com­pa­nies tend to catch up to large ex­ist­ing prod­ucts, at least in terms of fea­tures. The new product with a small code base needs to in­vest much less in fight­ing back de­pre­ci­a­tion (i.e. legacy code), so they can add new fea­tures much more quickly.

If you’ve worked in a soft­ware startup, you’ve prob­a­bly ex­pe­rienced this first hand.

Con­versely, as the code base grows, the pace of new fea­tures nec­es­sar­ily slows. De­creas­ing marginal re­turns of new fea­tures meets up with in­creas­ing de­pre­ci­a­tion load, un­til adding a new fea­ture means aban­don­ing an old one. Un­less a com­pany is con­stantly adding en­g­ineers, the pace of fea­ture ad­di­tion will slow to a crawl as they grow.

Pre­dic­tion 3

Since this all de­pends on de­pre­ci­a­tion, it’s go­ing to hit hard­est when the soft­ware de­pre­ci­ates fastest.

The biggest fac­tor here (at least in my ex­pe­rience) is ex­ter­nal APIs. A com­pany whose code does not call out to any ex­ter­nal APIs has rel­a­tively light de­pre­ci­a­tion load—once their code is writ­ten, it’s mostly go­ing to keep run­ning, other than long-term changes in the lan­guage or OS. APIs usu­ally change much more fre­quently than lan­guages or op­er­at­ing sys­tems, and are less stringent about back­wards com­pat­i­bil­ity. (For apps built by large com­pa­nies, this also in­cludes call­ing APIs main­tained by other teams.)

Redis is a pretty self-con­tained sys­tem—not much de­pre­ci­a­tion there. Redis could eas­ily add a lot more fea­tures with­out drown­ing in main­te­nance needs. On the other end of the spec­trum, a mort­gage app needs to call loads of APIs—credit agen­cies, prop­erty databases, gov­ern­ment APIs, pric­ing feeds… they’ll hit equil­ibrium pretty quickly. In that sort of en­vi­ron­ment, you’ll prob­a­bly end up with a roughly-con­stant num­ber of APIs per en­g­ineer which can be sus­tained long term.