Unpublished Software Components are Technical Debt
Or, why you should stop treating engineering like artisanship
In "The Art of Doing Science and Engineering" Richard Hamming recommends engineers do not use special-purpose chips unless they are essential elements of the design.
But what is it that we front-end and full-stack engineers do each and every time there is a new problem to be solved? Why, we create a special-purpose React component, of course! Often driven by a special-purpose API. We don't even stop to consider the question -- so easy and expressive is our toolkit that each component and each API can be hand-crafted with care (or disregard), turning each of us into a more or less sophisticated artisan of User Experience (often less, in my case). This begs the obvious question: are you an engineer or are you an artisan?
For intentional artisans, the current state of affairs presents no problem -- fun and expressive tools fit artists perfectly. An artist first has a vision and then asks "how can I express my precise vision, at any cost?"
Engineers, however, need to care deeply about cost, reliability, maintenance, testing, integrations, edge cases, and troubleshooting. The engineer starts with a problem and a budget and asks "how can I solve the problem, as inexpensively and simply as possible? ideally forever and universally." Therefore, the components of our solution should not simultaneously be engineering and art, or we will make an embarrassment of both.
Art does not seek to be cheap, simple, productive, or universal. Engineering does not seek to be expressive or visionary. This article is about engineering only, artisans may disregard, unless they seek greater productivity.
So what is to be done, if I am looking to build well-engineered systems?
the simple costs and benefits of software
Well, there are 3 main costs in software components:
Initial design costs
Initial implementation costs
Troubleshooting and maintenance costs
Relatedly, there are 2 main benefits of your components:
Expected, designed uses
Unexpected, future uses (and integrations)
It is obvious we want to minimize costs and maximize usefulness of our components. For that reason I propose some general principles:
general principles for designing components:
Always use general purpose, well-tested components when they will solve the problem
This entirely removes cost categories 2 and 3
It sets you up for integrations + new features
AI fully understands your interfaces, for free, and can build, implement, and alter the components for you, further decreasing costs.
Other users will find errors, weaknesses, cross-browser compatibility, accessibility, responsive design, and the 268,444 other aspects of component design (literally)
Other users will write documentation, so your engineers don’t have to
Other users will suggest upgrades so you can expect a steady stream of improvements with little or no effort on your part
You will keep getting the benefits of innovations and new standards
If general purpose components cannot solve the problem, publish the design spec for a new component to solve the problem
This exposes you to unexpected upside. Perhaps users will help build new features. Perhaps some random person on Upwork has already solved the problem.1 Perhaps there is an open source lib you’re missing.
AI can read about your component and teach future engineers how to use it.
Your component can become a new standard, and now you and your company are engineering leaders who set new standards for software!
Never build unpublished, custom components unless they are essential and proprietary aspects of your design.
You bear all the costs, with no unexpected upside, only designed upside
General-purpose AI models cannot help you or future team members
Your component will be obsolete in 2 years
why do you need to go off the standard?
Before you commit to a custom component for all time, ask:
Are you doing this for "efficiency"? To save a few MB of library?
This is often the worst possible reason, given the scaling of internet speeds. 5G already achieves average download speeds well above 150 mb/s, with capabilities up to 20GB/s, and a 6G proposal is on its way, which could easily peak above 1TB/s. The largest web libraries are often cached on your user's device already. Pretty soon your web-app could be 10GB of standard libraries and it would not matter. Plus, most modern libraries support tree shaking, so you only bundle the functions you actually use. A single roundtrip request anywhere in your loading logic will delay first paint by easily 10x any library that you choose to include. In the long run, modern, modular libraries with tree shaking will be smaller than your custom code.
Are you creating a custom component to meet brand or style requirements?
This should be a red flag for your design system. Consider switching to an off-the-shelf design system with theming for your engineered applications, and a CMS / website builder for marketing + brand sites. Remember that constraints breed creativity -- a custom style system might seem great and artisanal at first, but even sweeter is solving real problems quickly.
That's all! If you have any thoughts on the topic, please do leave me a comment.
— Andrew Rose
P.S. I'm trying to figure out what I want my "standard stack" to be for web development now, and the innovation lately is overwhelming! Currently, I'm looking at Remix, Supabase, Tailwind, and Shadcn/ui (though the style is a bit corporate for my tastes :9). I like my stack to be as open source as possible, so I can benefit from users improving the software, and AI being able to model it. I shy away from JS when I could rely on core web features (hence Remix instead of Next). Tailwind has always made me a bit skeptical, but it was tailor-made for an era of AI models. I think I'd like a component library that is more web-native, and less React than Shadcn/ui, but I love how it vendors components for me automatically.
This is a true story. When I was telling my friend Jon about this article, he mentioned that at his company they had a problem related to PDF manipulation that would have required substantial engineering. They wrote a well-scoped doc and put out a contract listing on upwork. An engineer who specialized on this problem and had solved it 100s of times game them the code they needed for $40. That’s basically free. That buys like 20 minutes of a software engineer’s time, after benefits.