I wonder how old you are because this comes off a bit immature, but you also cite "being told for decades..." which implies more experience. Regardless though, it's pretty clear that the dimensions of intelligence that you possess and value are not really focused on the social and human dynamics side. This is going to be limiting in most company environments where there will always be a mix of people, with different viewpoints and different skillsets, and the only way to succeed and have impact is to figure out how to achieve goals in that environment. And I'll give you a hint here, getting other people to be like you and think like you is not the way. If everyone understood what you understand and could do what you do then what unique value are you bringing?
In general, the way to succeed and grow is to focus on what you can control, build relationships and collaboration with the highest performers you can find. Don't waste your time hand-wringing about lost causes. Talk is cheap, anyone can criticize, and there are always plenty of mentally weak people ready to join in a pity party about how fucked everything is. Do NOT let yourself develop a victim mentality. Instead figure out how to do the biggest and best things you can, and if your current environment doesn't support that, then go find a new one.
> Yes there are some domains where going from the time from zero to a working product is critical. And there are domains where the most important thing is being able to respond to wildly changing requirements
I agree with your observations, but I'd suggest it's not so much about domain (though I see where you're coming from and don't disagree), but about volatility and the business lifecycle in your particular codebase.
Early on in a startup you definintely need to optimize for speed of finding product-market fit. But if you are successful then you are saddled with maintenance, and when that happens you want a more constrained code base that is easier to reason about. The code base has to survive across that transition, so what do you do?
Personally, I think overly restrictive approaches will kill you before you have traction. The scrappy shoot-from-the-hip startup on Rails will beat the Haskell code craftsmen 99 out of 100 times. What happens next though? If you go from 10 to 100 to 1000 engineers with the same approach, legibility and development velocity will fall off a cliff really quickly. At some point (pretty quickly) stability and maintainability become critical factors that impact speed of delivery. This is where maturity comes in—it's not about some ideal engineering approach, it's about recognition that software exists to serve a real world goal, and how you optimize that depends not only on the state of your code base but also the state of your customers and the business conditions that you are operating in. A lot of us became software engineers because we appreciate the concreteness of technical concerns and wanted to avoid the messiness of human considations and social dynamics, but ultimately those are where the value is delivered, and we can't justify our paychecks without recognizing that.
Sure it’s important for startups to find market traction. But startups aren’t the majority of software, and even startups frequently have to build supporting services that have pretty well-known requirements by the time they’re being built.
We way overindex on the first month or even week of development and pay the cost of it for years and years thereafter.
I'm not convinced that this argument holds at all. Writing good code doesn't take much more time than writing crap code, it might not take any more time at all when you account for debugging and such. It might be flat out faster.
If you always maintain a high standard you get better and faster at doing it right and it stops making sense to think of doing it differently as a worthwhile tradeoff.
Is it worth spending a bit more time up-front, hoping to prevent refactoring later, or is it better to build a buggy version then improve it?
I like thinking with pen-and-paper diagrams; I don't enjoy the mechanics of code editing. So I lean toward upfront planning.
I think you're right but it's hard to know for sure. Has anyone studied software methodologies for time taken to build $X? That seems like a beast of an experimental design, but I'd love to see.
I personally don't actually see it as a project management issue so much as a developer issue. Maybe I'm lucky but in the projects I've worked, a project manager generally doesn't get involved in how I do my job. Maybe a tech lead or something lays down some ground rules like test requirements etc but at the end of the day it's a team effort, we review each other's code and help each other maintain a high quality.
I think you'd be hard pressed to find a team that lacks this kind of cooperation and maintains consistently high quality, regardless of what some nontechnical project manager says or does.
It's also an individual effort to build the knowledge and skill required to produce quality code, especially when nobody else takes responsibility of the architectural structure of a codebase, as is often the case in my experience.
I think that in order to keep a codebase clean you have to have a person who takes ownership of the code as a whole, has plans for how it should evolve etc. API surfaces as well as lower level implementation details. You either have a head chef or you have too many cooks, there's not a lot of middle ground in my opinion.
I hear you, and agree there’s not much overhead in basic quality, but it’s a bit of a strawman rebuttal to my point. The fact is that the best code is code that is fit for purpose and requirements. But what happens when requirements change? If you can anticipate those changes then you can make implementation decisions that make those changes easier, but if you guess wrong then you may actually make things worse by over-engineering.
To make things more complicated, programmers need practice to become fluent and efficient with any particular best practice. So you need investment in those practices in order for the cost to be acceptable. But some of those things are context dependent. You wouldn’t want to run consumer app development the way you run NASA rover development because in the former case the customer feedback loop is far more important than being completely bug free.
I always try to design for current requirements. When requirements change I refactor if necessary. I don't try to predict future requirements but if I know them in advance I'll design for them where necessary.
I try to design the code in a modular way. Instead of trying to predict future requirements I just try to keep everything decoupled and clean so I can easily make arbitrary changes in the future. Some times a new requirement might force me to make large changes to existing code, but most often it just means adding some new stuff or replacing something existing that I've already made easy to replace.
For example I almost always make an adapter or similar for third-party dependencies. I will have one class where I interact with the api/client library/whatever, I will avoid taking dependencies on that library anywhere else in my code so if I ever need to change it I'll just update/replace that one class and the rest of my code remains the same.
I've had issues in codebases where someone else doesn't do that - they'll use some third-party library in multiple different components and practically make the data classes of that library part of their own domain and have workarounds for the library's shortcomings all over the place so when we need to replace it or an update contains breaking changes or something like that it's a big deal.
There's a lot of things like this you can do that don't really take much extra time but makes your code a lot simpler to work with in general, makes it a lot easier to change things later etc. It has lots of benefits even if the library never gets breaking changes or needs to be replaced.
Same thing for databases, I'll have a repository that exposes actions like create, update, delete etc and if we ever need to use a different db or whatever it's easy. Just make a new repository implementation, hook it up and you're done. No SQL statements anywhere else, no dependency on ORMs anywhere else, I have one place for that stuff.
When I organize a project this way I find that nearly every future change i need to make is fairly trivial. It's mostly just adding new things and I have a place for everything already so I don't even need to spend energy thinking about where it belongs or whatever - I already made that decision.
Well said. This summarizes my experience quite succinctly. Many an engineer fails to understand the importance of distinguishing between the different tempo and the immediate vs long-term goals.
No, the reason is because there are different tradeoffs, and things that work well often trade off other things.
For instance, Dan is one of the best writers for illustrating a general point with dozens of rapid fire small examples. This gives the point credibility in a way that a writer who might craft an easier-to-digest narrative doesn't have, but can be tiresome for those don't like the style.
> Not in my opinion. These kind of companies ask you to give 100% of yourself every single day.
No, I understand can understand how someone would come away with that conclusion based on common patterns in the industry, but I think you're conflating two things.
First, companies want teams that deliver value. If evaluation is based more on vibes than actual output, that's a management failure. Its nuanced, because bad vibes can really hurt teams a lot, so it's not that vibes don't matter, but if the source of the vibes is that management is clueless and doesn't understand the pain points on the front lines, then that's the bigger issue. As a junior engineer it can be hard to distinguish, but just understand that good management exists and is out there, it's not all pointy hairs (even if the language is sometimes the same, after all pointy hairs have to cargo cult off of someone).
Second is how much a company asks of you. Once you rise up the ranks in any sizable company you'll realize an incredible amount of effort is wasted just due to the overhead of coordinating many individuals. In these types of environments it's more about focusing on the right things rather than overall effort. As Steve Jobs said, "It doesn't make sense to hire smart people and then tell them what to to , We hire smart people so they can tell us what to do". Entire teams can be net negative because no one calls out the elephant in the room. While it's true, that if you are successful in a high performance org, more will be asked of you, it's also true that good leaders value solid contributors even when they set some boundaries.
So there are really two strategies in a large org: one is to do the bare minimum and keep your head down, in that case avoiding high performing orgs is probably a good idea. But at the end of the day your entire job and career is at the mercy of the beancounters waking up one day and realizing you are dead weight. Alternatively, you can adopt a growth mentality, do your best every day, and see where it takes you. The latter path does mean you might need to set some boundaries and say no to people at times. Sure it's awkward, but in the grand scheme of things this builds a stronger network and sets you up for long-term success far better than playing defense because you see the employer-employee relationship as fundamentally adversarial. I think that worldview makes sense from a physical labor workforce, but it's really self-defeating for software engineers because at the end of the day our output is what our code does (hence every IC is "managing" the tech output), not how much time we put in creating it.
This article buries the lede with a bunch of horrendous strawmen. The actual thesis is that software craftsmanship should be applied in the same way at all stages of a company. This is a very dangerous line of thinking that has killed many a startup staffed by experienced who engineers who are working in large scale systems and teams. I've seen it many times, and TBH it's not just an engineering problem, it's that people who have only worked in large companies don't have a real sense of what is truly essential, and so a huge percentage of their practices are effectively cargo culted over without any real reflection.
In the case of engineering, you need to apply a lot of judgement based on the situation that the company is in—how much runway, how much traction, actual product goals, etc. You must keep things as simple as possible at all times to optimize for future optionality as you search for product-market fit. All code is a liability, and you must fight tooth and nail against any individual who is getting ahead of their skis in terms of losing focus on the next thing needed to prevent the company from dying. The absolute worst thing you can do is bring some journeyman engineer and don't give them enough scope and ownership to satisfy their brain capacity or you'll end up with ridiculously over-engineered systems that impose a huge velocity tax for what needs to be a very lean and agile phase. I say this disclaimer first because 99% of people in tech trying to do a startup will fail by trying to do too much too soon, and have no intuitive sense of how narrow the tightrope from 0 to 1 success really is.
Of course that doesn't mean you shouldn't focus on code and system quality. Absolutely you should have tests, but you should apply serious judgement onto the nature of the tests in light of your best predictions about the future. You should think about what code is foundational, and what decisions may be one way doors, but not obsess over leaf nodes and experiments that are just as likely to be abandoned or scrapped as they are to be built upon. Making these calls is tough—no one can predict the future—but long tenures in fast growing code bases helps. Seeing the impact of ones decisions 2, 5, 10 years down the line is eye opening, experience is useful here as long as one still thinks from first principles and doesn't just rely on rote practices because they are comfortable.
I think you are actually agreeing with the main thesis of the article, which I agree buries the lede and obfuscates it with overly focusing on TDD in particular. You're just adding the, accurate, caveat that most people's opinions on how to build good software are wrong.
I think that it's true that at large companies there can be an obsession with over-design and over-engineering "for scale", but I actually think that's wrong to do at large companies too, you're just less likely to pay the ultimate price for wasting time on it.
The overall article is ambiguous enough that yes, it can be interpreted to be in alignment with my values. But also based on my quarter century experience in both startups and Fortune 500s and the transition from the former to the latter, I would say for every engineer shooting from the hip and creating an unmaintainable mess there is an equal number who will read it as a justification for over-engineering. Also, though of course most startups fail, I would say the latter archetype fails at a higher rate because they are focused on the wrong things.
The crux is really is the nuance of this statement: "The disciplines that lead to successful software are always valid". This is tautological, everyone reads and sees what they want to see. But if we take the examples he gives, that's where judgement comes in. Double-entry bookkeeping, yes, that's pretty universal. TDD? That really depends on what you are doing and what value you get out of it. Not only do specific disciplines and practices vary based on company stage, they also vary based on the product and the goals of the business. Anyone who doesn't understand this is fucked if they try to do a startup.
Agreed, but I would say it isn't necessarily tautological. I think there are lots of people out there who think that if you want to develop software faster you cut corners on things like testing (to be clear I'm not endorsing TDD, just saying having a reasonable number of tests beats the hell out of none), choosing a dynamically typed language vs a statically typed one, and just generally throwing code over the fence for the sake of moving fast. I think it's true that you will in fact move faster over time (not even a very long time, just like a few months) if you stick to whatever good principles you would have stuck to if you didn't have the time pressure of shipping while at a startup.
My read was that this was intentionally vague since it's basically trying to say to the reader that whatever you think is a good idea too do while developing software at a non-startup is also a good idea to do at a startup. If the reader has bad ideas about developing software then clearly there's no helping them :P
Exactly. All those things that are critical in a startup are really good way to accelerate possibly stagnant development. However, short-sightedness and ignoring tech debt kills startups too
what a ridiculous comparison. Of course a superhero movie is more entertaining than a film that is explicitly designed to avoid mass appeal.
The nuance is that movies today is not where the most creative talent is directed anymore. The shift started with prestige TV taking off in the 2000s, and episodic content on streaming services surpassing film as a mass-market artform in the 2010s, with the pandemic driving the nail in the coffin.
I loved the late 2000s / early 2010s superhero movies. Spiderman, The Dark Knight, Iron Man, etc. These were great films. Today, the MCU is just eating its own tail with the most bland, repetitive crap. It's all designed to incentivize the same die hard fans to keep forking over their hard-earned cash with all the cross-film teasers and the need to watch every film to understand all the references and moving parts. I understand the business model—it's actually the same as comic books now—because people don't casually go in to see random movies anymore, they do that at home on Netflix, so they have to target the repeat viewers. It's visually impressive, and the acting is good enough to keep a relatively large subset of the population coming back, but for someone like me who wants at least a little bit of novelty or creativity in the plot or characters, it's just so become so mind-bogglingly boring.
I would argue that in fact you do need a critical mass of ego throughout the ranks of engineers in a large organization. An engineering culture where engineers don't care inevitably leads to abdicating responsibility for overall system health and even data/workflow correctness as PMs attempt to steer the ship without understanding the implications of what they are asking for. Once this has gone on for a while, the system will be so intractably broken, and the dead sea effect will have caused such a brain drain of all those capable of fixing the problems, that at some point there's no economical path to restoring the software systems to a healthy and maintainable state. At that point you might as well just call in the private equity guys and figure out how to extract maximum cash out of the business as it stands, because any code change becomes more likely to break more things than it improves.
So yeah you need ego. That said, it must also be tempered with the reality of needing to compromise enough to satisfice all stakeholders (including both technical and business stakeholders of all the different flavors). The beautiful thing about software engineering though, is there is a reasonable amount of objective facts, metrics and tradeoffs, that given a critical mass of sufficiently skilled and mature engineers, common ground tends not to be too hard to align on. Or at least, far easier than to get a non-technical stakeholder to understand the long-term implications of a bad decision.
To make the best work you have to take pride in it. This work has my name on it, customers will use it, I want to improve their lives not make them worse.
The developers who come after me will need to assimilate this work. They'll need to build on it. I want to make their work a joy, not a burden.
Do I take pride in what I do. It's not enough that the code just compiles.
Pride is balanced by humility. I'm prepared to defend my choices (with well informed, well experienced) answers. But the choices I make are always a balance of upside and downside. And over my career things have changed which reweights some of those decisions. I'm open to external input from others because I want the result to be good not me to be right.
I want to be proud of my work. But I'm humble enough to let others help me to improve it.
I think FOSS really only works well for infrastructure type of projects, things where the customers are downstream programmers or at the very least very technical sysadmin types and power users who can understand the gory details to some reasonable depth. Those projects work because they are centered around proven abstractions that are broadly applicable, thus allowing for a tight charter and some stability in requirements.
End user software by comparison, has not really been successful in the FOSS model. There have been many attempts, but they perenially lag behind commercial offerings, and thus primarily see adoption from the ideologically motivated and/or very cost sensitive users.
reply