However not all legacy projects are bad to work on. If they're decently developed, then often you'll find that most of the pain is in setting up your local codebase. e.g. sorting out make files, or header file clashes etc. And if you're lucky, some poor bastard has already done the hard work for you.
As an aside, I know tons of experienced "Senior" developers who just suck at their jobs. The problem is they have tons of experience in delivering terrible products based on terrible code and architectural decisions. They just never had anyone to show them any better. And now that they're "Senior", noone can tell them anything. Many devs who work in corporates understand this pain. Shoutouts to my peers who have to "fix" 3000 line stored procs with a 100 line "change control comment saying stuff like "2009-04-03 JS Added TAX_REF_ID". I live your pain.
EDIT: Also, if you happen to work at company that thrives on terrible legacy products, try to drill it into their heads that BAU Support is not part of the solution. Every time I've raised the issue of the mounting tech debt I've gotten the response "Business does not have the apetite to solve these issues. And why would they? That's why we have support teams".
1. green field project 2. other people's legacy project 3. your green field project growing into legacy project.
You can learn so much from each of these, but to me the most eye opening experience was our green field project growing into a project with more and more developers.
You could learn so much about others, some were very arrogant, went on constant refactoring mission only to mess up everything. If for some reason, I couldn't check what they did, usually, I had to come in and fix their stuff, but sometimes the only person knowing about the edge case was me, so I just left it "messed up".
Others tried to understand why the system ended up this way, some accepted it, while the best actually improved the system by looking back and recognizing the simplicity hiding in the mess.
My experience, is that this is usually a defensive shell around personal insecurity.
On the outside, it looks the same, but internally, insecure people can be reached (not easy), whereas truly arrogant folks (a lot more rare than you might think) cannot.
My experience is that most difficult people are actually decent folks, that we can enjoy working with, but we need to adjust to them, and they need to adjust to us.
> a defensive shell around personal insecurity.
could just be despair and honesty.
I was reading about the kindle os here and wonder how many people are facing giant hairballs in their professional life.
> the Kindle’s OS is a mess of Java, Javascript, Lua, Native and React Native code. ...
> The Kindle runs stripped down Linux with a React Native frontend along with Java backend applications.
> The Kindle runs stripped down Linux with a React Native frontend along with Java backend applications.
Honest question: what exactly is wrong with this setup?
There's the OS, there are services and user land apps written in java and misc native code, and from the link there's a single component where Lua was used as bridge code.
The GUI is React Native, which is just a framework to simplify the design and implementation of native GUIs.
So exactly what's wrong?
Preferred stack: The one they know.
How? Any advice?
I have learned to make the first move, myself. It doesn’t work, if I insist on them doing it. Also, I need to accept their value system. If they truly have values that I can’t deal with, I usually “compartmentalize” things, so I don’t deal with them, in those areas.
I’ve spent my entire adult life, getting along with people that don’t play well with others. I haven’t always been able to get through, but I’ve had more success than failure.
It feels like working with all sorts of people (and sometimes working around them) is an important skill that’s not usually focused on enough, which is how you get people that might be brilliant but also are really hard to work together with.
TL;DR: Normalize healthy dynamics by example.
Obviously just my 2c and every case is different etc.
- Share credit and be generous with sharing
- It is better to error in the direction of over sharing credit than under sharing (taking credit for yourself)
- Slow down, say "I don't know" (good to follow up with "but I'll find out")
- Saying "I don't know" is often a better answer than guessing
- Guesses are fine, but should be prefaced and be clear that you speculate BUT will follow up and verify
- Not knowing is the first step to knowing. You've turned an unknown unknown into a known unknown. That's a big step! And often one of the hardest!
- Encourage juniors to speak up and not be afraid of raising issues, even if those senior to them disagree.
- Either the junior provides valuable feedback that was missed (everyone is fallible) or it is a learning experience for the junior and //we invest in juniors//.
- Seniority does not make the dev, the logic/ideas/thinking does.
- Mentor more. I think we often want to send juniors to their corner so we can concentrate on our work, but you have to train those who will later on become your peers.
- There are no graybeards without noobs
- Encourage more collaboration
- Especially when there are complementary skill sets
- Drive culture that recognizes that ones ideas are not a reflection of their value. What I mean is that it is okay to be wrong. The idea is distinct from you, and you are more than just one idea
- We're all wrong, it is just a matter of "how" wrong
- Having a "bad idea" or "wrong solution" doesn't mean /you're/ wrong/bad.
- Recognize that we're all on the same team. Criticism is constructive
- A critique is not a dismissal, it is because we are all on the same team trying to make the best thing. An outside view is often helpful to finding issues. We work as a team, so criticism is debugging as a team, just as you criticize your own work when initially formulating ideas (you reject some ideas, find limits, and formulate plans. Criticism is the collaborative version).
- This means also be careful how you hand out criticism. Ensure it is always constructive. It always contains an explanation and a path forward (as much as possible).
- Competition can be beneficial, but it can easily create tribalism. Fight the natural tribalism because we're all on the same team (helps to have team mixing)
- Make time for learning
- The job always involves learning, no matter how senior you are. If there is only time for "doing" and no "learning" then you will only gain debt and you especially are teaching juniors the wrong lesson.
I would state that the first point can be a bit tricky. I've worked in some environments where one or two people follow the point and no one else does, so this ends up with those people being seen as "non-productive." This is more representative of poor management though and I'd push back against the toxicity to the extent possible.For managers: the last point is really important. Your coders shouldn't always be sitting in front of their IDE or terminal typing code. They should be spending a fair amount of time reading docs, writing docs, and googling. Writing docs is actually important as it helps communicate ideas, which also serve as reports to YOU. This communication also helps de-abstract the ideas and often leads to finding issues. It is an important step to debugging. (It is often a good idea to have juniors build or append docs when they are onboarding. Dual serves as the intro to the codebase)
That's a very old pattern.
That's one of the reasons I write stuff that no one reads[0]. Writing up a technique forces me to research it, and break it down into its simplest components.
> One of the best ways for me to learn, is to explain.
Kinda The Feynman Method[0] or even Rubber Ducky Debugging[1]. I found that teaching in grad school has immensely helped. Not only do you have to solidify concepts to a much higher degree but you revisit stuff too. I think there is a big difference between the level of expertise needed to be comfortable working with some knowledge vs what is needed to teach that knowledge. The former is much more personal. But when learning any topic there's always things you had to table, that you didn't answer, because you had to make general progress. But if you don't revisit these, then they always remain unknown. Revisiting often brings new insights and thinking that you probably didn't expect. Any time I've revisited a topic, even ones I know quite well and have revisited many times, I often find that there's still more for me to learn. There's infinite depth, so always keep your eye out.You do all this upfront design about how it's going to work and then "Oh god there's so many splinters and sharp corners, it's only getting worse and every new team onboarded to the framework needs to have their hands held, and management won't allocate time to address the tech debt."
The exception… is for any feature I added myself. If I spent a lot of energy releasing v1 with the “perfect” (to me) abstractions, etc. I get paranoid about more junior developers messing it up for new features. Re-reviewing code changes, insisting on all the upfront refactors and rare edge cases handled, etc. Something I need to overcome.
the new teams doing some cool stuff with my old baby
This is always a fun one...
"Who wrote / designed this garbage... Oh wait, it was me"
I see this constantly on HN and have had to learn to not engage with those conversations. It is like some people have never had to run a project under any sort of external constraints like head count restrictions, layoffs, inheriting a system which you didn’t write, or awkward organizational structures.
Some will scream until their ears bleed that there is only one optimal way to do things and if you aren’t doing things that way you are a incompetent chump — totally disregarding the reality that people have to work with.
I would say the same about a sales person who can still manage to have a great year even when the (their) market is cratering. Again, I have seen it a few times, and it always impresses me... watching them pull a rabbit out of the hat!
I think the term is the issue. Senior development means something more intrinsic than it does in other title, like a senior manager. I think what we're attempting to define is something closer to seasoned developer.
I'm fully aware of that, which is why "Senior" is in double-quotes, but experienced (aka "seasoned") is not. My point is that you can be seasoned at delivering bad products. The point about seniority just speaks to tenure at a company. Sure, you can join a company as a "Senior dev", but that's not quite what I'm referring to here. One would think that they would be exposed during the interview process, but alas, we all know that's often not the case.
But this will never be an issue in the real world
- Also Random_Rockstar_Dev_254
I see this a lot too. Which I think I have a good analogy: judging a program by its output is like judging a math proof by its last line. But math proofs are really hard and annoying because you can divide by x somewhere and then have to contend with the fact that x cannot be 0 or else you have to divide by 0, making your proof invalid. The proof is only valid in its entirety. I see programs in the same way. But this is exactly why Test Driven Development is so naive. You can't just write tests to check your correctness, it only works under the assumption that you can predict all possible classes of data that will be processed and in which way. Currently and in the future. The experience of a senior should certainly make TDD far more effective, but I'd say a graybeard is one who knows the limitations and is able to write code that is efficient, can per-emptively address future tests, and writes in such a way that the code can be easily modified and adapted for the future changes.If I've learned anything in my history of coding it is that where the program ends up is never where I expect it to. My experience makes me better at predicting that differential, but to be honest, given a big enough project you are never really going to be able to predict the end state. Things change that are out of your control.
I'd also argue that this is why it is so important to write documentation and comments while coding. It'll help you later on and anyone that is onboarding (small cost to document, but many people reap the benefits). I don't want a junior coming in and completing a task fast, but I want them to come in and learn the codebase and how to adapt it for the task. Sure, it isn't as fast, but juniors are an investment anyways. I think we have to be honest that no matter what we do we are either investing or taking debt, both of which compound. But I think the problem is that interest is hard to observe and debt is less observable than the direct cost seen in an investment. Maybe a real Sr Dev is the one who is more aware of these trade-offs?
this should never happen...but it's happened to me[0]
We've all been there lol[0] https://github.com/thehackerwithin/illinois/blob/5f91a29b1d4...
It's a classic tragedy of engineering; you can actually ship with almost anything, and just successfully delivering a product reinforces a feeling that your choices were good, no matter how arbitrary they were in practice.
People can get really confident that what they're doing is tried and true, while it's simply the only approach they've had for all of their career.
Yes, and the flip side to this is the other kind of senior that you mention in your comment: the one that refuses to believe that things can be done better.
It's hard to find the right balance between wanting to write better code and pragmatically dealing with the realities of a huge, messy legacy codebase with tons of bad decision - you can't fix everything (and certainly not at once), so you have to choose your battles wisely.
That all said, it's all subjective, blah blah the end.
Hey at least they’re using iso 8601 dates!
"Why is it hard to maintain?" is a question every architecture person should be able to answer...especially after digging into some large, old piece of software.
And once you have a semblance of knowledge about how the thing works and how to make fixes, you've built a small moat around your career there.
I'm under pressure, I have to deliver, things get rewritten many times, edge cases are found and fixed in production after they slipped through multiple qas but need to be fixed instantly or we lose money.
It happens.
But personal code? I am almost always proud of my work. It shows in general I was not under pressure and enjoying building it.
Even my code from the first year of programming, it shows it was made to make a point in writing a nice solution.
I may cringe at times, but still proud.
Context does matter, and code quality is more often than not a byproduct of our emotional state rather than skills.
Theres always something that needs to be rushed out the door at work and honestly nobody pays me for pretty code.
But my own projects? The code is a large part of why I do it .
"I can't ever get even with past-me, and I'm already on thin ice with future-me"
I posted recently about how I divorced not too long ago. I'm in an apartment now (for the first time in 16 years) while I stack up cash to build a new house.
I always think I'm not making any progress but then I see my next door neighbors ordering DoorDash 15+ times per week, and I realize that I'm doing pretty damn good for myself.
Or I walk by the mirror and think about how I've neglected my lifting for the past 6 months while I finished up this SWE degree. But I'll see those same neighbors struggle to walk up the stairs and I again realize that I'm making some awesome choices compared to those two folks.
Well crafted dig.
It's easy to view smart coworkers as superpeople... until you're reminded that they, on occasion, make dumb human mistakes too.
A benefit of being a sr dev is getting the benefit of the doubt on mistakes, so I might as well use that to my advantage and let everyone learn from them, not just me.
Of course, you have to have a culture where making a mistake doesn't immediately lead to being let go or publicly reprimanded. I have definitely learned quite a few things about people and code by helping juniors work through bugs that may not have been so obvious (including occasionally finding bugs in 3rd party libraries).
I used to suffer from imposter syndrome significantly. It helped to see the mistakes others made which anyone could have made. It also helped to have my own good ideas and talent explicitly acknowledged. Learning to not take criticism personally and own your own mistakes also helps to rob imposter syndrome of its power because it demonstrates to others that you can be forthright and self aware, and that helps build trust.
Operationally, people gravitate toward trying to treat the humans as machines.
We have machines for that.
- time pressure, leading to just making it work and not caring about edge cases, which were bolted on later. Or maybe never at all. And naturally, there is no useful testsuite.
- the prototype ended up in production
- a refactoring happened, but there was no follow-up into other areas of the code base
- features changed, but the code is still influenced by the necessities of the old way it worked. Similarly, for changing technical circumstances
- code was copy-pasted or was worked on by too many people
- code was herded too long by the same person and now, to put it nicely, bears traces of their bespoke coding style
Chesterton's Fence is still very relevant, therefore I'd still err on the side of writing a lot of high-level tests before touching anything.
[0] https://en.wikipedia.org/wiki/Fundamental_attribution_error
I found it true for the first five years into my career, now it’s still true but with a longer time scale, which I suppose is progress
There’s also legacy code that is not great and is not solving a complex problem but is just a product of the conditions at the time. It’s easy to look at that and assume you can do better given the same constraints, and this is neither educational nor esteem-building. Just an ego massage.
Here, parent explained in detail how to get stuff done, management very happy and secure their position for years to come.
Truth is harsh, however this seems to be 100% accurate for nearly all cases of employment. Rarely do you get to focus on simply interesting problems and good engineering as a primary concern
word of warning from an old guy, don't create problems for yourself when you don't have to. Turning something boring into something interesting can have painful consequences down the road.
>One needs to be able to create their own interesting solutions rather than expecting them to be handed down on a plate.
You should tell that to every old manager I had.
They sound like boreful people. If you find you have become boreful, there's a good chance you may be experiencing burnout or depression, which are nasty diseases, but still ones that afflict the subject, not the object. Nothing is boring but for the person who perceives it that way.
They have a job and family at the end of the day too. Not everyone has the power nor passion to try and and move a billion dollar corporation to care for people over profit.
>If you find you have become boreful, there's a good chance you may be experiencing burnout or depression,
I haven't has a full time job in over 18 months, so I hope I'm not burnt out.
>Nothing is boring but for the person who perceives it that way.
That's like saying nothing is ugly except for what people think is ugly. Boredom is personal and shaped by each person's experience, so I don't think framing it as a person for not trying hard enough is going to go too far in practice (nor is it productive to judge people based on how they prioritize their lives). Of course some things will be boring to one person and a life passion for another.We don't have enough time nor energy to try and find appreciation for every object and concept on Earth.
What a strange thing to say! Aren't people also kind of... things? Shouldn't they be at least as interesting as things?
Fixing stuff on a legacy product may make management happy but if that product is discontinued next year then you haven't accrued technical expertise valuable to the company (but you may have built a reputation as a fixer and quick learner).
So, as usual, it is a balancing act.
Edit: this is my perspective from the embedded world. It probably applies generally, though.
When times get tight the new projects get shitcanned and the 10 year-old cash cow design gets the promised new features.
One crusty project I worked on was a legacy control board for a piece of restaurant equipment. The customer, the company that built the actual machine, had been building this product for 40 years. It had been through two PCB redesigns and two different microcontrollers, but the logic was tried and true and had to survive. A port of the project from 6800 assembly to C had completely gone off the rails and the contractor was dumped. All it took was a 20-opcode fix to a routine that the contractor just couldn't grok.
If any the conclusion is: work on what you want, life is short.
The split there isn’t in favor of doing stuff that’s fun and novel though; actually, the engineer should usually pick a boring proven solution if the public has a high stake in the outcome.
But the vast majority of HN topics are not concerned. Additionaly, it does not pay well
That's a perfectly reasonable thing to want out of engineering for yourself. I wouldn't state it as an absolute truth for all people though.
Personally, I'd like to be working on something that extends the state-of-the-art a little, even if only by a tiny fraction. It can be one for the other disciplines involved - it doesn't have to be the software I'm writing that is responsible for that (and it usually isn't), but that's what I derive satisfaction from.
I find improving established code of far more interest and reward than greenfield. Coming to understand what a person was thinking while building something is fascinating. My updates are tangible life improvements to real human beings. That interests me, at least.
Greenfield, I have the incredibly stressful job of making all the major architectural mistakes people will complain about for years to come. I'm not interested in that. There's also a much higher chance with greenfield that the product will just fail and no one will enjoy your work.
And you've found a way to completely forestall the non-technical demands that force non-ideal design choices?
You're sitting on a gold mine friend.
I am sitting on a gold mine. But I can’t prove it. That’s the problem with this stuff. The industry moves horizontally with each design such that nobody knows if say the current design trend was better than the last. We go better or we go worse and nobody knows if it actually was.
Maybe I am sitting on a gold mine. The problem is you’d never know about it. You’d never believe it. Because a proof isn’t possible.
So because it’s not provable you have no clue about any design whether one is better or one is worse and thus you disbelieve everything.
The overall question is how do you create a design or pattern that has maximum adaptability to anticipate any possible requirement change with minimal rewrites? Where you only swap in and swap out modules without changing structure?
I think I found the best possible way. But you won’t believe me.
I have a solution for custom content management systems that usually makes it fairly easy for me to incorporate customer requests without needing to toss out my existing solution and start over. Almost a type of framework built over an existing open source CMS product. I maintain about 90 websites as the only backend developer, and have spent about 12 years now working through various versions of the software to custom tailor a solution that works for nearly all situations I have come across. I don't do front-end development any longer, but I was involved heavily at the beginning stages to make sure both the front-end and backend were flexible and broken apart into modules.
If you find a templates solution works for you then that is not a general solution. It’s better for you but you are hardening and becoming opinionated on certain details and that works for what you’re working on. My solution is a lower level pattern that is more general and less opinionated. For what you do it likely is only effective if you are required to drastically change an existing project while maintaining as much existing code as possible. The solution prevents a complete rewrite and allows you to modularly pull out what you don’t need and replace with the things you do need.
If you don’t need 90 percent of your project this pattern won’t prevent that. What this pattern allows is for you to actually pull out that 90 percent. Many projects that only need you to pull out say 30 to 40 percent of it actually end up with a full rewrite because the modules aren’t decoupled enough.
I do have something that explains my pattern and serves as support for it. But it's no proof because such a thing isn't really provable. Anyway it's easier to let AI explain it. In the following conversation I basically ask chatGPT about this topic with very general questions while manipulating it as little as possible.
https://chatgpt.com/share/67b3ca4b-fc5c-8001-a072-342357b8a6...
chatGPT basically arrives at the same pattern I discovered. Where I cheated is when I started giving my own opinions on the several patterns chatGPT suggests and I hinted the LLM at the direction I wanted to go. Everything else is a very general question.
That being said, there are clearly other sets of general questions that can very well lead chatGPT to form alternate conclusions, but this one aligns with what I and many others have discovered.
The point is I don’t want to argue about it with someone who doesn’t get it.
Why? Because I very quickly made a name for myself as someone who learns quickly and then gets stuff done even in contexts where everyone else struggles. Solving problems that have long been considered intractable is a good way to show your value.
Nope, assuming that code works and handles UTF8 (sorry, but there is a point where it is too far gone to save). You’ve made yourself essential. You know the business better. You are strategic. And often, you become the one person who knows the legacy code base. And you will quickly find yourself a part of super important decisions. You’ll be called out regularly for fixing things that have gone unfixed for years. You’ll also drive value by helping the company avoid spending vast amounts of money re-implementing functioning code. On top of that, new product is often a mix of new and old, so you’ll quickly find yourself in the middle of new product teams.
Even at a young age, some people find it super fascinating to work with old stuff and study the traces of people long gone.
If you're that kind of person, digging into an old assembly codebase is going to be so much more fun than learning some newfangled web framework.
The most interesting work is going to be 10% interesting and 90% not interesting. In most cases it will be less interesting than that.
Prioritizing "interesting" work means you'll always be exploited by the companies that can sell you on less pay, less benefits, less time off, less respect in exchange for some pittance of perceived novelty. Don't fall for this trap.
We sometimes get incidents that things someone remembers should exist do not work, only to find out that that product was retired several years ago (but someone forgot to delete some DNS records, so there's an error message from... someone's server!). But only because we asked half the company on Teams.
... I literally had a multi month ticket open to track down an expense for something I either never used or only used once which ended up being a load balancer or some crap
I found that working on legacy projects is often the most interesting.
There is the challenge of understating the original code base, which may look messy, but is full of history, you see the architectural mistakes and their consequences, how they managed to fix obscure bugs, tackle unexpected use cases, different code styles,... Then they ask you to implement a feature or fix a bug, you have to find what can be touched and what can't, what is a bug and what is a feature, see how much time you have, and decide on a strategy. This is production code and people are using it right now, and they are efficient with it, you have to help them and not force them to relearn everything for no good reason.
And there is technical debt management, your goal is to make the code better or at least not worse, but without introducing breaking changes and under time constraints. You have to manage your off time to make improvements that will help you in the future (chose wisely) as the next truckload of tickets arrive. It includes having a good relationship with your managers as no one wants you overworked, as it would just cause technical debt to accumulate with no time to repay it.
And good luck getting an AI to do that, if you want job security in the age of LLMs, that's your change. It doesn't mean you can't use AI, but you certainly won't be replaced by it.
I’d love to hear how others deal with this inevitability.
One of the old specialty C ERPs I've worked on has literally buried more than 3 (the ones I know of) "great Ruby rewrites". There are just _so many_ features, that the newcomers always ended up on the chopping block after 5-10 years and before they could achieve real customer traction. The old project just ended up getting some more interface fixes instead (aka even more features to catch up to) and to this day the compiler for the core hasn't even been upgraded to a version that can run on anything newer than XP SP1 (the improvements in SP2 break it).
Now they're doing another modernization project with some web services and stuff. We're happy to take their money for those as well. I'd be quite shocked if those do anything other than enlarge the moat of the core.
Well, we just spend 5 years training the team on how to build the old refresh, and now the new refresh can be done properly.
The "on borrowed time" is an odd conclusion.
When someone takes over, it's typically a priority change ("We want everyone working on new projects, just freeze this old stuff") rather than some kind revenge path against anyone who worked successfully on the old stuff when it was a priority.
And a good engineer who is able to handle legacy code will typically be even better when working with greenfield code, because the latter is easier, all else equal. So unless you are refusing the priority change, why would you be on borrowed time?
Long-term ownership and careerist jockeying don't go well together.
source: endured this while working at a startup that was bought out by SFDC. Kept my principles, but lost the job. /shrug
The irony is that for those of us who enjoy maintaining, improving, and eventually replacing legacy stuff (code, systems, infrastructure, etc), what we really want is often just stability in career - hence the moat. It means it’s cheaper to keep us around for the long haul than rehiring us again at current market rates every few years, especially if we’re making ends meet with a high quality of life. But that’d mean focusing on the long term, which I don’t see many companies doing in general.
Over here in the UK, if I constantly chase the new hotness I will have plenty of new startups to choose from but I’ll almost always be low-balled on pay with some practically meaningless equity package.
Gaining more in-depth experience opens up the better paying positions further up the ladder in more established companies.
Don’t want to keep your head in the sand for years but at the same time, there are a lot of skills that can’t be learned if you stay exclusively at the bleeding edge.
One pre-FAANG tech company I worked had a policy (known among the local schools) that each new hire has to "pay your dues" on grunt work, before you get to do fun/glamorous work. That kinda worked for them.
A problem they had was that they were doing desktop development in C and C++, which, among other purposes, had to ship software bundled with new brand-name PCs (so not necessarily able to bugfix over Web later). The real problem was that very few new grads are minimally competent at production-grade C or C++. But, in early Web years, the company couldn't hire enough experienced software engineers to relocate to a college town. So they had to try to train and retain local grads of a top CS program. That might've been part of the thinking behind "pay your dues"... before you get to make rookie mistakes with greenfield.
Important note: building the moat means you can one day be promoted to principal and make junior webdev money
I've been with my company for a while now. Too often I find someone come in with the "this is trash and all of you are idiots" mentality. I've typically not been keen to help such folks. They usually soon ask for rescue from the very same dumpster fire they created.
Developers need to realize that AI means that a lot of coddling our profession used to get is going away. Employment has always been a tenuous relationship with a fundamentally sociopathic entity, but things will be getting a lot worse in the future.
> Experience is what you get when you don’t have any.
The only better experience than working on a legacy codebase is working on a greenfield project long enough to watch it become legacy and see the good and bad consequences of past decisions.
"The general tendency is to over-design the second system, using all the ideas and frills that were cautiously sidetracked on the first one."
Brooks reasons that the combined experience of doing the first project well and the second project badly leads to better designs from then on.
Code is a pure liability that you accept to get a useful service.
Not that I game that obviously, it just occurred naturally for a ~4 month period
How 'bout job security?
But when it was finished it failed to meet the basic requirements for the only customer that used the system.
One of the tells of an inexperienced engineer I use is how much they disparage the previous team's work.
And many fail to discern between "disparage" and "critique" or even "Question in order to learn"
One of the greatest failings I've seen in leadership in our time is the idea that in order to make a critique one must come with a solution in hand. As a leader I want to know the things that are going wrong as soon as they're seen, not to require someone to go through the heavy lifting of a solution before they say a word. Now, of course, there's a difference between bitter unhelpful cynicism, and simply identifying a gap between the current state and optimality.
I think I’ve had the conversation with new to my organization devs a few hundred times: “Look... saying code is crap or stupid is telling others you’ve given up on learning. How about asking why it is the way it is?”
> greatest failings I've seen in leadership in our time is the idea that in order to make a critique one must come with a solution in hand.
The pattern works at very high levels in an org chart, but with developers and those that manage them it breaks the whole concept of problem solving. You have to be able to identify and understand problems before you can come up with a solution... and usually, with software, the solution is developer hours.
It really isn't. A lot of the time you end up spending a lot of effort to understand something that was dumb to start with and has been dumb ever since. Something like the bullshit asymmetry principle applies - any idiot can take 5 minutes to write a line of code that will baffle a team of experts for hours. (I've done so often enough myself).
When your v1 code takes a company to profitability it was good enough.
Edit: oh and how could I forget as simple and readable as possible
I'm not granting a "rewrite from scratch in Rust" exception even though that's in vogue right now. I'm not saying don't rewrite it in Rust, I'm saying don't rewrite it from scratch. It's harder to write new features in Rust while maintaining the old C code, but it's the right way to do it.
The latter is less costly and only requires you "only" to open your eyes and look at projects that are in an ugly state the right way. Yet surprisingly few people are capable of looking at someone elses fucked up project and not going all like: "Hah! Idiots! I would never have made that decision".
Maybe however that crusty piece of code used a framework that — back in the day — was the hottest, trendiest piece of technology out there and you are currently in the process of committing similar sins, and you won't know it till it is too late.
For me adminstration of Linux servers has been an invaluable source of inspiration. You are directly and 100 percent exposed to the effects of software aging in a changing environment. And you directly wittness which software ages like fine wine and which ages more like milk.
Now I'm curious. What software has aged well? What software hasn't? Do certain types or categories of software tend to age better or worse?
There is no teacher quite like cause and effect.
No, growing as an engineer just means the reality of software development will make you more miserable.
> I think jumping around can risk creating an engineer who leaves a place worse than when they started.
Of course it does, but companies have made the choice to pay more for that than for someone who stays and makes the place better, we should give them what they want.
These aren't mutually exclusive.
> If you like your employer and colleagues and are growing as an engineer isn’t that better in the long term?
Better than liking your employer and colleagues and growing as an engineer and also having more money? No.
This isn't a field with low demand for talent. You can have all these things--don't settle for less.
> I think jumping around can risk creating an engineer who leaves a place worse than when they started.
That's a risk that companies should be willing to pay to avoid.
But a lot of companies aren't willing to given their engineers adequate pay increases each year, and it's not engineers' responsibility to accept less money than they are worth. If you want more experienced engineers, you have to pay for more experienced engineers. That's just basic free market economics: if you don't like that you don't like capitalism.
Some companies understand this and do better, but these are the exceptions not the rule. If you find such an exception, stick with them!
I fundamentally disagree with the narrative that engineers are supposed to be passionate about their craft and learning and interest and not care about the money. That's propaganda spread by corporations to get us to accept less pay, and if you believe it you'll be exploited. And as it turns out, the places that pay the best are also usually the best places to be passionate about your craft, so there's no real conflict. The places that pretend to pay you in learning and passion instead of money generally don't deliver on the learning and passion either.
Having the context or, better yet, responsibility for the past decisions is great for developing a pragmatic approach to software design AND empathy for other software engineers.
But someone who has used a palm sander, orbital sander, belt sander, disc sander, and so on will be able to see a new type of sander in the context of existing tools and know what’s right for what job.
Your average junior will be in the first scenario. However, it’s possible to be experienced yet still be stuck in the first scenario.
Do you mean that there are seniors with a lack of experience? I believe yes I was also making that same point.
Ageism in the industry also encourages seniors to appear to be plugged in with the newest shiniest thing, so we kind of do it to ourselves with our hiring practices.
We need to divorce seniority from talent.
This phrase is divorcing seniority from talent, albeit perhaps not in the way experienced engineers might prefer.
This is an organizational problem.
I’m actually kind of struggling to imagine a scenario where a junior developer has the agency to slap entire frameworks onto existing systems. Maybe a late stage startup? Coding practices, definitely.
We are currently going under modernisation converting Perl to Python. It's what it is, but I've encountered developers coming along and wanting to throw bulky frameworks at when all you require is to process the output result of a vendor issued binary.
There have been novel ideas but just unpractical for the causes.
It's madness getting through to them when all you require is a replica of the current system and not a rewritten feature set not related to the scripts at hand.
Using the latest framework improves their future job prospects. Whatever company they work for will lay them off in a split second and won't provide anywhere near the comp increases that a new job would. So why should they care about the long term of a project?
Companies have no loyalty to employees which means employees have no loyalty to companies.
Because your future networking potential—and therefore future job prospects—depends on being liked by your peers, who may not choose the same moment of departure as you would. Follow standards and work towards long-term maintainability not because you see yourself at the same company in 2 years but because your coworkers might see themselves there and you want them to like you and feel good about recommending you to their network.
Why?
Why would they want to help you along? They burn capital referring you, they burn even more when you make a mess of things. When you leave a burning husk behind everywhere you go your own network of people who trust you is minimal, so you're not an asset to have around them. Why would they want to help you instead of finding people to con who can actually be an asset to them? In this entirely self-centered world you've put together, what's in it for them?
Referring people for no other reason than that you know them works great, since it will implicitly happen in reverse too. See e.g. the Freemasons.
> They burn capital referring you? They burn capital referring you, they burn even more when you make a mess of things.
They burn capital if hiring you seems like a failure, they win if it seems like a success. That means your incentives are aligned. No-one gets dinged for hiring someone who implemented a couple of buzzwords, left after a couple of years for a higher position elsewhere (or got promoted into management), and then the project they were on failed a few years further down the line for unrelated reasons. Quite the opposite.
You're the one sacrificing your own self-interest for pride and are upset that others refuse to do so.
edit: And I get it, I don't optimize my own career like this, focusing more on what I enjoy to work on at the moment. But I don't begrudge people who do optimize nor do I claim to be better than them. If anything they are acting in a much more rational and organized way to their goals than I am.
And why should someone be respected if they are intentionally making others lives more difficult like that. That's just hypocritical.
Legacy projects are usually have shoestring budgets for any changes. After all, they are legacy for a reason: likely nobody has touched the system in a while. I can't think of a single legacy client I have that would be okay with a company wasting their hours on adding a new framework. It can be hard enough to convince them to let us add tests or upgrade from PHP 5.6
Nah. Over the long term the lava layer pattern is the only way to stop the mental load of the project from growing indefinitely. You will ultimately have to keep up with modern development practices, because keeping everything on standards from 2010 will ultimately be even worse, and the longer you leave your migrations the more painful they will be.
Let's keep going with this SQL-injection prone legacy framework, with zero automated tests for the entire codebase!
I've hit that, for real. Sometimes you really do need to move mountains to change legacy.
Given this, I tend to prefer a single, formatting-only commit when introducing formatting standards to an existing codebase. Otherwise, it’s difficult to take advantage of QOL features like auto formatting in your editor, or other formatting tools which tend to operate on entire files. Then PRs end up being mixed with formatting changes, which adds friction to the review process.
https://www.stefanjudis.com/today-i-learned/how-to-exclude-c...
These safeguards save so much time that there should be no excuse for not having them.
The first one is more forgivable - sometimes that's just the problem space. But neither of them are conducive to fulfilling working environments.
Perhaps in the end you are not a senior engineer until you've done a bit of everything.
Any code that's old enough to have its first birthday party is "legacy", which means that "legacy" is a completely useless category. Anyone calling anything "legacy" is generally just showing their own lack of experience.
An alternative definition, legacy code is any code where there’s no one left on the team who has been working on it for years and intuitively knows the pitfalls. Then everyone’s scared to touch it or make big refactors, which actually leads to those small special cases being added instead.
When joining a legacy project, most of the hardest work has already been done for you.
Of course this assumes there is a way to introduce improvements at all. Just keeping the lights on isn't as satisfying.
But seriously, everyone tries to move these goal posts. We still have no good idea.
One in particular worked for a sort of "side ways competitor". They were horrified to hear about my deploying a fix all by myself, no review, for a legacy product that really didn't have a good testing environment. It was inconceivable to them.
On the other hand, not long after, our organization of 4 coders put out a better product in 3 months than their massive organization did in 18 ... and using that some "inferior" legacy systems that just so happened to be better suited to the job than some fancy new framework.
This sounds like a great way to cause a live-site incident.
Having said that process and bureaucracy is still what they are and they tend to eat up more cycles as time goes on, even if only because they're administered by humans.
No review though? Aaaa
The biggest challenge is of course trying to prevent those feelings becoming contempt, rage and resentment as you see the same mistake repeated over and over again by one person who should know better while they make funny little comments in their commit messages while writing bugs that end up causing data corruption a year down the line.
The trick is mindfulness meditation and definitely not going to LinkedIn, printing out a copy of their face and then ripping it up while screaming.
I'll sometimes chat with the people who have had to maintain whatever ground work I had laid, and each time around, I get a little bit better at laying down a framework of infrastructure and operations that's better and better at staying sustainable
By your reasoning, looks like you are still a junior /s
Senior engineer has come to terms with feeling like an idiot all the time and now seeks this feeling because that's where the progress lies.
Nothing else to it.
But an interesting article for modern times.
"This is wrong!"
Well, we actually do it that way because Frank told us to do it that way
"Frank isn't even here anymore! That doesn't make sense! Let's just do it the right way."
Three Weeks Later
Details please ;-)
1990: https://www.ibiblio.org/apollo/Documents/MCC%20Development%2...
Pirates: https://www.wbs.ac.uk/news/the-nasa-pirates-that-transformed...
Head Pirate: https://historycollection.jsc.nasa.gov/JSCHistoryPortal/hist...
WEX/Display builder/etc: Even when I was there my young peers weren't too keen on legacy/back-end understanding. GUIs were the cool thing. I worked on something that touched many legacy subsystems so got to work with a lot of "dinosaurs" (what the Pirates called them).
MCC testing was all AM shift, pirates/youts up front, retired controllers in the back. Test MOC took 30 minutes to recycle. I always sat in back if they'd let me.
"The only thing left was to deploy it so that we could test it. This was a problem, of course – in this case, we were not the ones deploying it or testing it. Deploying had to be done by the ops team, and testing by the test team. Why couldn’t we, as developers, manage the whole feature from start to production instead of opening tickets and waiting for other teams before we could close our task?
Firstly, we couldn’t avoid manual testing since much of the code wasn’t covered with tests. We also couldn’t deploy the application ourselves since the infrastructure didn’t allow us to.
This had us thinking about the reasons behind the separation of duties and how our current approach is better. Seeing that our lead and cycle time on this project was significantly higher than usual (it took us weeks to deliver something we usually deliver in several days), the evidence was strongly in favor of that."
and nothing beats the legacyness of languages before the java/webtech times - and im not even talking about COBOL
So much code that looks weird that I had dig in a lot and finally understood why this tiny piece of non-sense code is actually what make the whole system working, and think about how smart the previous dev was and how dumb I would have solve the problem myself.
After enough time, none of these HR provided labels matter and the work you do signals your level, not the other way around. Just a fair bit of warning though, regardless of your level, you should know how to implement the solutions from the ground up without corporate support and IT setting everything up for you.
I really recommend "Working Effectively with Legacy Code" By Michael C. Feathers. It is a top notch read, and helps newcomers (and vets alike) approach these legacy systems.
They did something which I thought was brilliant: they gathered a list of ALL of those projects and assigned every single one of them to an existing engineering team, whether or not that team had any relevant knowledge or experience (which was true for most of these older projects).
No team could complain about this because every other team was suffering through a similar challenge.
I think it worked really well! Bugs in older systems now had obvious owners, and teams were incentivized to dig in and figure out how the crusty old code they were now responsible for worked.
I think it’s less awful if your expectations are not that of a new project. Yes, there’s a ton of ugly bits at the interface level that you can’t fix. You make it as comfortable as possible while figuring out where to go next if anywhere.
Unfortunately, we had this level of luxury because we had growth, especially new business scenarios that involved thousands of companies and millions and millions of users, thanks to the confluence of internet, mobile, social network, cloud, and machine learning applications. I'm not sure we are going to have similar growth in the coming 10 years, but I hope I'm dead wrong.
[1] The downside, of course, is that engineers tended to abandon a project after version 0.1, like what Twitter engineers did to their Manhattan project.
Or a 6 paragraph Slack post in #general about what an amazing experience it was...to host a 30 minute Q and A on a certain topic. #community #growth #sharingiscaring
Data-centers -> Cloud Monolith -> API and Services Databases.... Memcache -> Redis JS/JQuery -> MVP Framework Git. and not to forget K8S.
and this is just the tip of the iceberg.
- none of the original authors are with the company anymore.
- it's written in a language/using a stack/using technology that is used absolutely nowhere else in the company anymore.
- it is big enough that migrating all of its functionality is a tremendously difficult undertaking, to the point where it's likely never going to get done, at least not in your lifetime (either in terms of your tenure at the company, or literally, before you die.)
Outdated architecture/code practices/patterns are a smaller smell, as is the lack of documentation/testing - but those can be present even in greenfield projects written by a big enough dipshit - and I say this having been that dipshit at several points in my career :)
The other members kept producing new code and debugging new code.
Disregarding of whether it's fair or not, there was a kind of balance that kept the boat floating.
I left since then and it wouldn't surprise me if the legacy was being rewritten from scratch (and over-engineered).
Back when people mandated an 80 character line width because their CRT monitors only had so much resolution
Engineers that refuse to acknowledge constraints, whatever the nature of those constraints may be, are not engineers. At best they are ideologues, at worst they are just incompetent. The most pathetic thing you can do is just continuously deny the laws of physics and reality, because it doesn’t suit you at some ideological level.
Truly elegant solutions are those that account for all constraints in the simplest, most concise way. It is those that do more with less.
in my almost 3 decades in this industry THIS has been the number one way to tell a Junior from Senior SWE. nothing else comes close 2nd
When it is something new, it is "easy" - check Stackoverflow or ask LLM and then modify code until it "works" - which doesn't even need any understanding of how it works. Just tweak until there are no errors and screen makes the manager happy.
Since corporations started cutting budgets - don't pay for training and get developers who know how to write CV and memorised interview questions and agree to work for substandard pay, it is all slowly going downhill. More experienced developers are tired of handholding new employees through basics or very much developing the tickets for them on top of their own workload and so they quit and problems are getting bigger and bigger.
As it turns out, if you're operating on large data in bulk you should probably just have a dense ID map and then do everything in arrays. Nothing beats arrays :)
Nothing better than somebody exclaiming, "what the hell were they thinking when they did this 20 years ago!" and the "they" is you.
Sometimes it is a pleasure and sometimes it feels distant and alien and frustrating.
-------------------
Axis one: quality of the code
Axis two: how many use the code
There are four quadrants:
-------------------------
Clean code no users: enjoy it while it lasts, it won't.
Clean code and users: what are you doing here, the code is done. Get out!
Dirty code no users: a waste of time
Dirty code and users: careers are made here.
I am proud that I am a part of a team that uses most of the best practices
Speak for yourself... I'm the exact opposite. Then again, outside of work I fix and maintain other physical systems which are many decades old, some close to a century, so perhaps that changes the perspective. I see so many in the software industry cargo-culting "best practices" dogmatically, creating constant churn via useless trendchasing, that they keep reinventing - badly - new ways to do the exact same things as before.
That's why software engineering is still very immature as a science field. It's pretty much the result of so widespread software engineers' inability to even define the problem they're trying to solve. Along with the inability to demonstrate the impact of the change and provide at least some cost-benefit analysis.
And this is spreading like a cancer. Unfortunately, I see this almost everywhere and I am not sure when it all began because we were certainly not being taught such things at the University. Critical thinking seems to be lost in a signal-noise ratio.
For example, maybe there's really crazy hard to follow code and the person who wrote it left a decade ago and there's no tests so it's too risky to refactor it but you can write new tests, really delve into the use case of the code and then refactor the original code.
Or, maybe the code is using a version of a framework that's 9 years out of date but you can incrementally upgrade it after gaining confidence in any way you can.
courage to be able to push changes you think could be breaking changes but always be optimistic that you to be able to fix as soon as possible. tests are ideal, but not everyone has 100% coverage. sometimes you just have to trust your gut and do diligence and push the change. don't be scared to push something because you don't know the behavior in production. the only way to know is to try.
A doctor who can cure patient illness from mallpractice are considered senior !.
Regardless their years of experience.
Oh, and my favorite software tale is when we turned on the new software in the control room and the first thing the plant manager said when he looked at the new digital instrumentation was "that can't be right".
I thought I needed to earn my place and tried very hard, but slowing down and going home at the right time regardless of whether the thing I was working on was done or not, would have been smart. They placed a lot of pressure on me to produce perfect code at the same rate as all the java devs based on the assumption the assumption their job was harder because it was backend, but the tools and parameters of their work were wildly better at the time, and I had undiagnosed ADHD.
So, I'd be protecting my sanity more and telling them it'll get done next week instead of trying to fit one more thing in because "it's just a button" or whatever.
> My feeling about legacy projects hasn’t changed – I still hate them (...) Instead of feeling resigned, we saw it as a place to ask questions and learn.
I see that you advocate embracing the opportunity to work on legacy projects, so I say even if you dislike legacy projects, try to avoid phrasing it so strongly. It might influence other devs in your team to develop a negative mindset toward them.
I'd also add "until you've spent some time testing a code base you didn't write" to the mix. That definitely tightens your senses and busts some assumptions.
The next time you hear the phrase "... well, it worked on my machine ..." it takes on a very different complexion.
A big problem I see in software is that developers don't stick around on projects long enough to see that their mistakes are mistakes. Instead, they move on to the next greenfield project or rewrite the project in the newest shiny tech without ever actually addressing the core complexity of the problem they're trying to solve.
If you can open up vendor documentation and the default solution works in your environment you are either lucky, or have gone through hell to get it there.
Excellent.
So do I qualify if almost every project that I created in my > 50 year career is now legacy?
(Might be made slightly easier as many of the companies no longer exist.)
But this is why technological choices need to be taken with care, and if you're in a managerial position, you should only agree on using new tech if there's dedication / guarantees for the ones pushing it, a healthy labour market, or budget to provide training for people that don't know it yet.
And that's why so much is still 'boring' languages like Java, .NET, PHP and Javascript instead of Rust or what have you.
I'm a Go fan though and will exclude that, as IMO Go is by design easy to learn by anyone.
I'd be happy to have a look at anyone's legacy code to at least write an analysis paper on it. (I'm currently seeking contract work, been doing code for a long time.)
I could work as fast as I deemed fit, because nobody understood what I was doing. Any feature could take a day or a month.
Good times.
It really helped to understand why SOLID principles are used.
I always considered legacy projects more interesting than zero to one. A lot less reinventing the wheel and bikeshedding, a lot of puzzles - painful ones, sure, but at least it's not a bunch of CRUD (or the usual startup experience, which is a constant flux of rewriting-crud-very-fast)
Oh and every subsystem shares dependency references that are only used by one subsystem.
Oh, and the C includes are wrong and overwritten by the shell scripts and make files.
GOOD LUCK
OH, and your team is too scared to use the simplified build because there are too many things they don't understand, even though you did hash comparisons for every built artifact.
Others:
Big code base. Big team. Big company. Different languages (and preferably in different paradigms; OO/FP and low/high-level). Different frameworks/stacks. And "in anger" also really helps IMHO (this means when shit went really bad and needs to be fixed fast).
Huh?