I agree that consistency is important — but what about when the existing codebase is already inconsistent? Even worse, what if the existing codebase is both inconsistent and the "right way to do things" is undocumented? That's much closer to what I've experienced when joining companies with lots of existing code.
In this scenario, I've found that the only productive way forward is to do the best job you can, in your own isolated code, and share loudly and frequently why you're doing things your new different way. Write your code to be re-used and shared. Write docs for it. Explain why it's the correct approach. Ask for feedback from the wider engineering org (although don't block on it if they're not directly involved with your work.) You'll quickly find out if other engineers agree that your approach is better. If it's actually better, others will start following your lead. If it's not, you'll be able to adjust.
Of course, when working in the existing code, try to be as locally consistent as possible with the surrounding code, even if it's terrible. I like to think of this as "getting in and out" as quickly as possible.
If you encounter particularly sticky/unhelpful/reticent team members, it can help to remind them that (a) the existing code is worse than what you're writing, (b) there is no documented pattern that you're breaking, (c) your work is an experiment and you will later revise it. Often asking them to simply document the convention that you are supposedly breaking is enough to get them to go away, since they won't bother to spend the effort.
Hopefully, you have a monorepo or something with similar effects, and a lack of fiefdoms. In that case, if the current way is undocumented and/or inconsistent, you make it better before or while adding in your new approach. If there are 4 ways to do the same thing and you really want to do it a different way, then replace one of those ways with your new one in the process of adding it. For extra credit, get to the point where you understand why you can't replace the other 3. (Or if you can, do it! Preferably in followups, to avoid bundling too much risk at once.)
A lot of inconsistency is the result of unwillingness to fix other people's stuff. If your way is better, trust people to see it when applied to their own code. They probably have similar grievances, but it has never been a priority to fix. If you're willing to spend the time and energy, there's a good chance they'll be willing to accept the results even if it does cause some churn and require new learning.
(Source: I have worked on Firefox for a decade now, which fits the criteria in the article, and sweeping changes that affect the entire codebase are relatively common. People here are more likely to encourage such thinking than to shoot it down because it is new or different than the status quo. You just can't be an ass about it and ignore legitimate objections. It is still a giant legacy codebase with ancient warts, but I mostly see technical or logistical obstacles to cleaning things up, not political ones.)
Thats not how software engineering works in a business setting though? Not a single company I have been in has the time to first fix the existing codebase before adding a new feature. The new feature is verbally guaranteed to the customers by project managers and then its on the dev to deliver within the deadline or you'll have much greater issues than a inconsistent codebase. I'd love to work in a fantasy company that allows for fixing legacy code, but that can take months to years with multi million line codebases.
> I'd love to work in a fantasy company that allows for fixing legacy code
You're not supposed to ask. It's like a structural engineer asking if it's okay to spend time doing a geological survey; it's not optional. Or a CFO asking if it's okay to pay down high interest debt. If you're the 'engineer', you decide the extent it's necessary
No. You discuss it with your manager, and you do it at the appropriate time. Having both created, refactored and deleted lots of technical debt over the past 25 years, trust me: you just don't get to go rogue because "you're the engineer". If you do that, it might turn into "you were the engineer".
What if you spend a week or month refactoring something that needs a quick fix now and is being deleted in 1-2 years? That's waste, and if you went rogue, it's your fault. Besides, you always create added QA burden with large refactoring (yes even if you have tests), and you should not do that without a discussion first--even if you're the founder.
Communicate with your manager and (if they agree) VP if needed, and do the right thing at the right time.
> No. You discuss it with your manager, and you do it at the appropriate time.
Sure, if you're not sure if it's the right thing to do, talk to your manager or TL. A good engineering manager can help. If your manager "would never allow" it, they're not a good manager. Even for jobs much more menial than engineering, a good manager recognizes that autonomy/trust are critical for satisfaction and growth.
If you're working someplace where you're "not allowed" to make the changes you "wish you could," you're doing yourself a disservice. Find someplace where you're not only "allowed," but expected to have (or develop) the judgement required to make these decisions.
To be clear: "the business" expects (and in the medium/long term requires) engineers to make these decisions themselves. That is the job
> If you do that, it might turn into "you were the engineer".
The correct solution would of course rather be "you were the manager". :-(
It's why Scotty was always giving longer estimates that Kirk wanted, but Kirk was also able to require an emergency fix to save the ship.
The estimate was building in the time to get it done without breaking too much other stuff. For emergency things, Scotty would be dealing with that after the emergency.
If your captain is always requiring everything be done as an emergency with no recovery time, you've got bigger problems.
Scotty manages his tech debt
Scotty is a fictional character.
His followers though, are REAL. (Unless declared integer.)
Thats also not applicable in a business setting. If you have multi million line codebase, you simply cant refactor within reasonable time. Also refactoring can cause issues wich then need further fixing and refactoring.
If I touch code that I am not supposed to touch or that does not relate to my direct task I will have HR talks. I'd be lucky to not get laid off for working on things that do not relate to my current task.
What field in software are you working in ?
Came from ERP development and now I am in webdev.
And then at some point the codebase becomes so unusable that new features take too long and out of frustration management decides to hire 500 extra programmers to fix the situation, which makes the situation even more slow.
As I understand, there is a balance between refactoring and adding new features. It’s up to the engineers to find a way to do both. Isn’t it also fair if engineers push sometimes back on management? Shouldn’t a civil engineer speak up if he/she thinks the bridge is going to collapse with the current design?
Often the problem with companies running the Feature Factory production treadmill too long is you have code supporting unused features and business logic, but nobody knows any more which features can be dropped or simplified (particularly after lots of employee churn and lack of documentation). So the problem is not so much technical debt, but product debt.
You can refactor, but you're also wasting time optimizing code you don't need. A better approach is to sit down with rest of the company and start cutting away the bloat, and then refactor what's left.
I was involved with a big rewrite. Our manager had on his desk the old system with a sign "[managers name]'s product owner". Nearly every time someone wanted to know how to do something the answer was load that old thing up and figure out what it did.
Eventually we did retire the old system - while the new code base is much cleaner I'm convinced it would have been cheaper to just clean that code up in place. It still wouldn't be as clean as the current is - but the current as been around long enough to get some cruft of its own. Much of the old cruft was in places nobody really touched anymore anyway so there was no reason to care.
> while the new code base is much cleaner I'm convinced it would have been cheaper to just clean that code up in place
I saw one big rewrite from scratch. It was a multi-year disaster, but ended up working.
I was also told about an earlier big rewrite of a similar codebase which was a multi-year disaster that was eventually thrown away completely.
I did see one big rewrite that was successful, but in this case the new codebase very intentionally only supported a small subset of the original feature set, which wasn't huge to begin with.
All of this to say that I agree with you: starting from scratch is often tempting, but rarely smooth. If refactoring in place sounds challenging, you need to internalize that a full rewrite will be a few times harder, even if it doesn't look that way.
I stayed at a place that was decades old, in part to decipher how they’d managed to get away with not only terrible engineering discipline but two rewrites without going out of business. I figured it would be good for me to stick around at a place that was defying my predictions for once instead of fleeing at the first signs of smoke. I’ve hired onto places that failed before my old employer did at least twice and I feel a bit silly about that.
I wasted a lot of my time and came away barely the wiser, because the company is spiraling and has been for a while. Near as I can figure, the secret sauce was entirely outside of engineering. If I had to guess, they used to have amazing salespeople and whoever was responsible for that fact eventually left, and their replacement’s replacement couldn’t deliver. Last I heard they got bought by a competitor, and I wonder how much of my code is still serving customers.
> I saw one big rewrite from scratch. It was a multi-year disaster, but ended up working.
90% of large software system replacements/rewrites are disasters. The size and complexity of the task is rarely well understood.
The number of people that have the proper experience to guide something like that to success is relatively small because they happen relatively rarely.
> "I'm convinced it would have been cheaper to just clean that code up in place"
Generally agreed. I'm generally very bearish on large-scale rewrites for this reason + political/managerial reasons.
The trick with any organization that wants to remain employed is demonstrating progress. "Go away for 3 years while we completely overhaul this." is a recipe for getting shut down halfway through and reassigned... or worse.
A rewrite, however necessarily, must always be structured as multiple individual replacements, each one delivering a tangible benefit to the company. The only way to stay alive in a long-term project is to get on a cadence of delivering visible benefit.
Importantly doing this also improves your odds of the rewrite going well - forcing yourself to productionize parts of the rewrite at a a time validates that you're on the right track.
Part of our issue with the rewrite is we went from C++ to C++. For an embedded system in 2010 C++ was probably the right choice (rust didn't exist, though D or Ada would have been options and we can debate better elsewhere). Previous rewrites went from 8 bit assembly to C++, which is the best reason to do a rewrite: you are actually using a different language that isn't compatible for an in place rewrite (D supports importing C++ and so could probably be done in place)
Whether you rewrite or refactor the code is not so much the point of my comment - it's more that you should first determine what you actually need, in consultation with the project stakeholders, get rid of whatever you don't need, and then you can decide whether you need to rewrite or refactor. Cutting away the bloat will give you a better perspective on that decision.
Personally, I would lean towards refactoring - a rewrite is the "declare bankrupcy" stage of technical debt and should only be considered in extremis. For example, the original codebase was written in ColdFusion and in 2025 you can't find any ColdFusion developers (or anyone in their right mind who wants to become a ColdFusion developer). But in any case, rewriting a trimmed down codebase is easier than trying to replicate features you don't need any more.
Rewrites are much like any act of self improvement. People think grand gestures and magical dates (like January 1 or hitting rock bottom) are the solution to turn your life around. But it’s little habits compounding that make or break you. And it’s new habits that kill old ones, not abstinence.
I worked with another contractor for a batshit team that was waiting for a rewrite. We bonded over how silly they were being. Yeah that’s great that you have a plan but we have to put up with your bullshit now. The one eyed man who was leading them kept pushing back on any attempts to improve the existing code, even widely accepted idioms to replace their jank. At some point I just had to ask him how he expected all of his coworkers to show up one day and start writing good code if he won’t let them do it now? He didn’t have an answer to that, and I’m not even sure the question landed. Pity.
The person who promised him the rewrite got promoted shortly before my contract was up. This promotion involved moving to a different office. I would bet good money that his replacement did not give that team their rewrite. They’re probably either still supporting that garbage or the team disappeared and someone else wrote a replacement.
That whole experience just reinforced my belief that the Ship of Theseus scenario is the only solution you can count on working. Good code takes discipline, and discipline means cleaning up after yourself. If you won’t do that, then the rewrite will fall apart too. Or flame out.
In the same way people go to their doctor or dentist or mechanic too late and prevention and sometimes even the best treatments are off the table, software developers (particularly in groups vs individually) love to let a problem fester until it’s nearly impossible to fix. I’m constantly working on problems that would have been much easier to address 2 years ago.
The issue is that management usually doesn't care. Personally I usually have about 3-4 days to implement something. If I can't deliver they will just look for more devs, yes. Quantity is what matters for management. New New New is what they want, who cares about the codebase (sarcasm). Management doesn't even know how a good codebase looks. A bridge that is missing support probably wouldn't have been opened to the public in the first place. Thats not correct for codebases.
It depends.
Most shacks built in one's backyard do not pass any building codes. Or throwing a wooden plank over a stream somewhere.
Just like most software doesn't really risk anyone's life: the fact that your web site might go down for a bit is not at all like a bridge collapsing.
Companies do care about long term maintenance costs, and I've mostly been at companies really stressing over some quality metrics (1-2 code reviews per change, obligatory test coverage for any new code, small, iterative changes, CI & CD...), but admittedly, they have all been software shops (IOW, management understood software too).
> The issue is that management usually doesn't care.
Neither do customers.
The product is an asset. Code is a liability.
Can't be held accountable for work conditions engineers dont have power over. If I dont have time to write tests, I cant be blamed for not writing tests. Especially now with hallucinating bs AI there is a whole load of more output expected from devs.
Recently I got an email that some severe security defects were found in a project, so I felt compelled to check. A bot called “advanced security AI” by Github raised two concerns in total, both indeed marked as “high severity”:
— A minimal 30 LoC devserver function would serve a file from outside the current directory on developer’s machine, if said developer entered a crafty path in the browser. It suggested a fix that would almost double the linecount.
— A regex does not handle backslashes when parsing window.location.hostname (note: not pathname), in a function used to detect whether a link is internal (for statically generated site client-side routing purposes). The suggested fix added another regular expression in the mix and generally made that line, already suffering from poor legibility due to involving regular expressions in the first place, significantly more obscure to the human eye.
Here’s the fun thing: if I were concerned about my career and job security, I know I would implement every damn fix the bot suggested and would rate it as helpful. Even those that I suspect would hurt the project by making it less legible and more difficult to secure (and by developers spending time on things of secondary importance) while not addressing any actual attack vectors or those that are just wrong.
Security is no laughing matter, and who would want to risk looking careless about it in this age? Why would my manager believe that I, an ordinary engineer, know (or can learn) more about security than Github’s, Microsoft’s most sophisticated intelligence (for which the company pays, presumably, some good money)? Would I even believe that myself?
If all I wanted was to keep my job another year by showing increased output thanks to all the ML products purchased by the company, would I object to free code (especially if it is buggy)?
Don't check in any code, only prompts. The product is reconfabulated on every build.
There will be companies founded on executing this idea.
Sounds like a dogma that got us (as industry) into this mess.
I’d rather say it’s an observation of real behavior. Customers of yours don’t buy code (unless it is your product) - they buy solutions to their problems. Thus, management and sales want to sell solutions, because that gets you paid.
Engineering is fulfilling requirements within constraints. Good custom code might fit the bill. Bad might, too - unless it’s a part of requirements that it shouldn’t be bad. It usually isn’t.
> A bridge that is missing support probably wouldn't have been opened to the public in the first place.
That's not always been the case and came to be because people have died... Is anyone going to die if your codebase is an unmaintainable mess?
Companies die because nobody is willing to work on the code anymore.
If VCs ever came to expect less than 90% of their investments to essentially go to zero, maybe that would change. But they make enough money off of dumb luck not leading to fatal irreversible decisions often enough to keep them fat and happy.
That doesn't sound nearly as bad or serious as people dying.
One: Have you ever tried to take a narcissists' money or power away from them? You would think you were committing murder (and some of them will do so to 'defend' themselves)
Two: All the stuff we aren't working on because we're working on stupid shit in painful ways is substantial.
That’s why consistent messaging matters. If everyone agrees to make features take as long as they take, then management can’t shop things around. Which they shouldn’t be doing but we all know That Guy.
When children do this it’s called Bidding. It’s supposed to be a developmental phase you train them out of. If Mom says no the answer is no. Asking Dad after Mom said no is a good way to get grounded.
Or it becomes so unusable that the customers become disenchanted and flee to competitors.
If management keeps making up deadlines without engineering input, then they get to apologize to the customer for being wrong. Being an adult means taking responsibility for your own actions. I can’t make a liar look good in perpetuity and it’s better to be a little wrong now than to hit the cliff and go from being on time to six months late practically overnight.
> As I understand, there is a balance between refactoring and adding new features.
True, but has drifted from the TFA's assertion about consistency.
As the thread has implied, it's already hard enough to find time to make small improvements. But once you do, get ready for them to be rejected in PR for nebulous "consistency" reasons.
They don't think they have the time, but that's because they view task completions as purely additive.
Imagine you're working on this or that feature, and find a stumbling block in the legacy codebase (e.g., a poorly thought out error handling strategy causing your small feature to have ripple effects you have to handle everywhere). IME, it's literally cheaper to fix the stumbling block and then implement the feature, especially when you factor in debugging down the line once some aspect of the kludgy alternative rears its ugly head. You're touching ten thousand lines of code anyway; you might as well choose do it as a one-off cost instead of every time you have to modify that part of the system.
That's triply true if you get to delete a bunch of code in the process. The whole "problem" is that there exists code with undesirable properties, and if you can remove that problem then velocity will improve substantially. Just do it Ship of Theseus style, fixing the thing that would make your life easier before you build each feature. Time-accounting-wise, the business will just see you shipping features at the target rate, and your coworkers (and ideally a technical manager) will see the long-term value of your contributions.
I am a solo dev for my company which is a semi profitable startup. Before I was hired the codebase was built by a hobbyist and remote workers. I was hired due to a language barrier with the remote staff. I barely have 4 years of experience so I really really dont have the time to fix shit. Currently I have to ship reckless without looking back. Thats upcoming tech companies for ya, will get worse with AI.
Expecting a developer with 4 YoE to be able to handle a messy legacy codebase by themselves is a suboptimal business decision I’d say.
New features are the right time to refactor. If you can't make the code not complete shit you don't have time to add the feature. Never refactor code to make it prettier or whatever, refactor it when it becomes not-fit-for-purpose for what you need to do. There's obviously exceptions (both ways) but those are exceptions not rules.
At least, that's what I teach our devs.
My company didn't even have time to keep the dependencies up to date so now we are stuck with Laravel 5 and Vue 2. Refactoring/Updating can be an incredible workload. Personally I'd say rewriting the whole thing would be more efficient but that's not my choice to make. If you have plenty of time for a task, I fully agree with you.
I was in an organisation that made decent money on a system built on Laravel 3, I think. The framework was written in an only static classes style, which they over ten years had run with while building the business so everything was static classes. Once you have a couple of million lines of that, rewrite is practically impossible because you need to take the team of two OK devs and a junior off firefighting and feature development for years and that will hurt reputation and cashflow badly.
My compromise was to start editing Laravel and implementing optimisations and caching, cutting half a second on every request within a month of starting, and then rewriting crude DIY arithmetic on UNIX epoch into standard library date/time/period functions and similar adjustments. I very openly pushed that we should delete at least two hundred thousand lines over a year which was received pretty poorly by management. When I left in anger due to a googler on the board fucking up the organisation with their annoying vision where this monster was to become "Cloud Native" on GCP credits he had, a plan as bad as a full rewrite, it only took a few months until someone finally convinced them to go through with deletions and cut LoC in half in about six months.
I don't think they do containers or automatic tests yet, probably never will, but as of yet the business survives.
I usually am in favor of a complete rewrite. I'd also prefer to not grow projects into multi million line monoliths. Just make multiple smaller ones that can interact independently with each other. Much simpler structure. Also safer in the long run.
You actually believe that? Distributed systems are simpler than monolithic? In PHP?
This business wouldn't exist if they attempted to follow your advice, because they weren't able and anyway didn't have the money to hire that many developers. There were a couple of subsystems they tried to implement the way you suggest, e.g. one for running certain background jobs.
It was a database table with one row per type of job and a little metadata like job status and a copy of the input. They started jobs by sending a HTTP request. This was a constant source of manual handling, because things started jobs and then crashed and never reset the status and things like that. You could respond that they should have used a message queue instead and so on, but the thing is, they didn't know how to build reliable distributed systems. Few developers do.
- [deleted]
It's still also often the right business choice, especially for small businesses which aren't making a profit yet.
- [deleted]
I don't disagree with what you've written at all, but let me just say:
> Hopefully, you have a monorepo or something with similar effects, and a lack of fiefdoms
ah to be so lucky...
It's a bit of a non-issue in this context. If you don't have a mono-repo, you should maintain reasonable consistency within each repository (and hope they're consistent between each other, but that's probably less important here).
Great points, I'd just add:
> A lot of inconsistency is the result of unwillingness to fix other people's stuff
Agree, so we find it best to practice "no code ownership" or better yet "shared code ownership." So we try to think of it all as "our stuff" rather than "other people's stuff." Maybe you just joined the project, and are working around code that hasn't been touched in 5 years, but we're all responsible for improving the code and making it better as we go.
That requires a high trust environment; I don't know if it could work for Firefox where you may have some very part-time contributors. But having documented standards, plus clang-format and clang-tidy to automate some of the simpler things, also goes a long way.
> That requires a high trust environment; I don't know if it could work for Firefox where you may have some very part-time contributors.
Ironically, that's why it works for Firefox. Contributors follow a power law. There are a lot of one-shot contributors. They'll be doing mostly spot fixes or improvements, and their code speaks for itself. Very little trust is needed. We aren't going to be accepting binary test blobs from them. There are relatively few external contributors who make frequent contributions, and they've built up trust over time -- not by reporting to the right manager or being a friend of the CTO, but through their contributions and discussions. Code reviews implicitly factor in the level of trust in the contributor. All in all, the open nature of Firefox causes it to be fundamentally built on trust, to a larger extent than seems possible in most proprietary software companies. (There, people are less likely to be malicious, but for large scale refactoring it's about trusting someone's technical direction. Having a culture where trust derives from contribution not position means it's reasonable to assume that trusted people have earned that trust for reasons relevant to the code you're looking at.)
There are people who, out of the blue, submit large changes with good code. We usually won't accept them. We [the pool of other contributors, paid or not] aren't someone's personal code maintenance team. Code is a liability.
> But having documented standards, plus clang-format and clang-tidy to automate some of the simpler things, also goes a long way.
100% agree. It's totally worth it even if you disagree with the specific formatting decisions made.
> All in all, the open nature of Firefox causes it to be fundamentally built on trust, to a larger extent than seems possible in most proprietary software companies.
Nice! We're still small so we somehow can keep that level of trust, but I always worry about how things may change for the worse as we grow. Mimicking the open source model as much as we can, even within a small private company, has worked well for us so far.
> > ... clang-format and clang-tidy to automate some of the simpler things, also goes a long way.
> 100% agree. It's totally worth it even if you disagree with the specific formatting decisions made.
So true! 5-6 years ago we had to make open source contributions to both clang-format and clang-tidy for several months to get them to support closer to our preferred style before we could get the "ok, close enough" buy-in across the company to implement automated formatting. (Mostly bug fixes for evidently rare flag combinations, but also a few small new features.)
In retrospect it was completely unnecessary - simply relying on automated formatting is sooo much better than any specifics of the formatting. I'm still glad we did though, as it made both tools better. We earned the maintainers' trust with a few early PRs, and remained active contributors for a while, but haven't contributed much lately.
(Posted on Firefox mobile... Thanks!)
I agree, but this presupposes a large comprehensive test suite giving you enough confidence to do such sweeping changes. I don't doubt Firefox has it, but most (even large, established projects) will not. A common case I've seen is that newer parts are relatively well covered, but older, less often touched parts don't have good coverage, which makes it risky to do such sweeping changes.
> Hopefully, you have a monorepo or something with similar effects, and a lack of fiefdoms. In that case, if the current way is undocumented and/or inconsistent, you make it better before or while adding in your new approach.
Unfortunately, this is how you often get even more inconsistent codebases that include multiple tenures' worth of different developers attempting to make it better and not finishing before they move on from the organization.
> In that case, if the current way is undocumented and/or inconsistent, you make it better before or while adding in your new approach.
Sometimes, but oftentimes that would involve touching code that you don't need to touch in order to get the current ticket done, which in turn involves more QA effort.
The need is quite widely interpretable though.
I worked on a Drupal site once where somebody had put business logic and database querying inside of template files.
Just because you can implement something without touching any other part of the codebase doesn’t mean that’s a good decision.
> If it's actually better, others will start following your lead.
Not really my experience in teams that create inconsistent, undocumented codebases... but you might get 1 or 2 converts.
It depends on the day but generally I believe that most engineers want to write good code, want to improve their own skills, and like learning and critiquing with other engineers. Sometimes a small catalyst is all it takes to dramatically improve things. Most of the times I've thought that individual contributors were the problem, the real issue was what the company's leaders were punishing/rewarding/demanding.
Exactly this. I (relatively recently) joined a team with a handful of developers all sort of doing things their own way. No docs, no shared practices, just individuals doing their own thing. After reviewing the code, submitted PRs with fixes, putting together docs for best practices, the entire team shifted their stance and started working closer together in terms of dev practices, coding styles, etc.
Not to say I got everyone to march to my drum -- the "best practices" was a shared effort. As you said, sometimes it just takes someone to call things out. We can do things better. Look at how things improve if you approach X problem in Y manner, or share Z code this way. Maybe the team was overwhelmed before and another voice is enough to tip the scales. If you don't try, you'll never know.
> I believe that most engineers want to write good code
But the opinion what makes code good differ a lot between software developers. This exactly leads to many of the inconsistencies in the code.
And that’s why you talk about it and agree on stuff. I call that being a professional.
doing some recent contract work I discovered someone putting this into a PR (comments my own)
```
let susJsonString = '...' // we get this parseable json string from somwhere but of course it might not be parseable. so testing seems warranted...
try { // lets bust out a while loop!
while(typeof susJsonString === 'string') { susJsonString = JSON.parse(susJsonString) }
} catch { susJsonString = {} }
// also this was a typescript codebase but all the more reason to have a variable switch types! this dev undoubtedly puts typescript at the top of their resume
```
I suppose this works?! I haven't thought it through carefully, it's just deciding to put your shoes on backward, and open doors while standing on your head. But I decided to just keep out of it, not get involved in the politics. I guess this is what getting old is like seriously you just see younger people doing stuff that makes your jaw drop from the stupidity (or maybe its just me) but you can't say anything because reasons. Copilot, ai assisted coding only further muddies the waters imo.
This is totally fine. If you're given shit data this seems like a reasonable way to try to parse it (I would personally bound the loop).
Typescript is not going to make it better.
The problem is whoever is producing the data.
I think the complaint here is they have a string, which even has the word string in the variable name, and they turn it into an object at the end. Hence references to Typescript.
I suppose what is wanted is something like
let parsedJSON = {}
try { parsedJSON = JSON.parse(susJsonString) } catch { //maybe register problem with parsing. }
That's quite different though. It looks to be dealing with the case that a serialised object gets serialiased multiple times before it reaches that point of code, so it needs to keep deserialising until it gets a real object. E.g:
I'd guess the problem is something upstream.JSON.parse(JSON.parse("\"{foo: 1}\""))
Hmm, yeah ok, didn't pick this out of the
let susJsonString = '...'
example
but evidently it is not just that it is serialized multiple times, otherwise it shouldn't need the try catch (of course one problem with online discussion of code examples is you must always assume, contra obvious errors, that the code actually needs what it has)
Something upstream, sure, but often not something "fixable" either, given third parties and organizational headaches some places are prone to.
Yeah. I imagine that's a bandaid around having to consume a dodgy api that they didn't have access/permission to fix.
The blanket catch is odd though, as I'd have thought that it would still be outputting valid json (even if it has been serialized multiple times), and if you're getting invalid json you probably want to know about that.
probably some times the api comes out with an empty string.
The code is either going to loop once and exit or loop forever no
Putting this in my web console:
I see:let susJsonString=JSON.stringify(JSON.stringify(JSON.stringify({foo:1}))) console.log("initial:", susJsonString); try { while(typeof susJsonString==='string') { susJsonString = JSON.parse(susJsonString); console.log("iteration:", typeof susJsonString, susJsonString); } } catch { susJsonString = {}; }
A comment explaining the sort of "sus" input it was designed to cope with may have been helpful.initial: "\"{\\\"foo\\\":1}\"" iteration: string "{\"foo\":1}" iteration: string {"foo":1} iteration: object {foo: 1}
It will stop when it gets something that's not a string due to
as it'll keep reassigning and parsing until gets a non string back (or alternatively error out if the string is not valid json)while(typeof susJsonString==='string') { susJsonString = JSON.parse(susJsonString);
- [deleted]
Now two of you are misunderstanding.
It’s applying the operation recursively.
Why the while loop
Because some upstream idiot is calling JSON.stringify several times.
I've seen this happen when someone's not familiar with their api framework and instead of returning an object for the framework to serialize, they serialize it on their own and return a string. Which then gets serialized again by the framework.
You came in so confident it was wrong, but it turns out you don’t really know what it does.
Please take a lesson from this. Good code is not the one that follows all the rules you read online. Your coworker you dismissed understood the problem.
I didn't know that JSON.stringify could be called multiple times on the same object and then unpacked via repeated calls to JSON.parse. So I was wrong on that. I think definitely this warrants a comment in the code, at the least explaining why this was taking place. The likely reason for the nesting was I think calling an LLM for a valid json object and somewhere in that workflow the json object was getting stringified more than once. I suspect this is the fault of the codebase and not the LLM itself, but it was typical of these devs to not ever investigate the api to understand what it was returning and rather just apply bandaid after bandaid.
I reserve my general opinion on the quality of this coder's work, as evidenced by the quality of the app itself among other things. But I guess you'd have to just trust (or not trust) me on that.
So no lessons learned?
Did you reply to the wrong comment?
I think asking questions is ideal. Even when I'm 99% sure a line is blatantly wrong, I will ask something like, "What is this for?". Maybe I missed something - wouldn't be the first time.
Darepublic originally posted his coworker’s code to make fun of above.
- [deleted]
Sure, but that does not imply they will follow whatever you found out to be the best for the piece of code you are working on right now.
>Not really my experience in teams that create inconsistent, undocumented codebases... but you might get 1 or 2 converts.
This has also been my experience. Usually there is a "Top" sticky/unhelpful/reticent person. They are not really a director or exec but they often act like it and seem immune from any repercussions from the actual higher ups. This person tends to attract "followers" that know they will keep their jobs if they follow the sticky person for job security. There usually are a few up and coming people that want better that will kinda go along with you for their own skill building benefit but its all very shaky and you can't count on them supporting you if resistance happens.
I've literally had the "I was here before you and will be after" speech from one of the "sticky's" before.
All these HN how to do better write ups seem to universally ignore the issues of power and politics dynamics and give "in a vacuum" advice. Recognizing a rock and a hard place and saving your sanity by not caring is a perfectly rational decision.
Well HN was created as a forum for discussing start up best practices, which is all about disrupting big companies weighed down by internal politics.
The linked article is about dealing with legacy codebases with millions of lines of code.
The response is accurate - anyone that's had to deal with a legacy code base has had to deal with the creators of said birds nest (who proudly strut around as though the trouble it causes to maintainability makes them "clever").
There are however some people who think they are sticky but aren’t really. Some but not all of them use Impostor Syndrome to keep their followers in line. You can recruit most easily from people they’ve left twisting in the wind when their suggestions and ideas turned out to not work, but only if you always deal with the poor consequences of your own decisions. People will follow ideas they don’t quite understand if they know they won’t be working alone at 7 pm on a Thursday fixing it.
These sort of people will vote for you publicly. However some lot them will still take the path of least resistance when you aren’t looking.
It was sort of a nasty surprise when I figured out one day that there are people in this industry that will agree with high minded sentiments in public but not lift a finger to get there. I ended up in a group that had two or three of them. And one day due to a requirements process fuckup we had a couple weeks with nothing to do. They just did the Hands Are Tied thing I’d been seeing for over a year (yes we should do X but we have to do Y for reasons) and I saw red. Luckily I was on a conference call instead of sitting in front of them at that moment. But I’m sure they heard the anger lines over the phone.
If the boss doesn’t give you an assignment, you work on tech debt they haven’t previously insisted that you work on. Simple as that. At most places if my boss disappeared, I could keep busy for at least three months without any direction. And keep several other people busy as well. If you don’t know what to work on then I don’t know what’s wrong with you.
I tried my best to offer a pragmatic recommendation for dealing with those sorts of people. I'd love to know what you would recommend instead?
IME it's politics, so you need to find someone that the sticky person fears/respects, and get them onboard.
The only other way I have succeeded is to appeal to the sticky person's ego, make them think that it's their idea.
Note: I have also had to deal with
Sticky person: Do it this way
Me: But X
Sticky Person: No, do it the way I have decreed
[...]
Three hours later (literally)
Sticky Person: Do it X way
This exactly. I worked at a place one time with a terrible code base. They based it on open source and slapped on additions with no style or documentation.
My first day, I couldn't even stand the code base up on my local dev environment, because there were so many hard-coded paths throughout the application, it broke (they were unwilling to fix this or have me fix it).
I tried to accept their way of coding and be part of the team, but it got too much for me. They were staunch SVN supporters. This isn't much of a problem, but we had constant branching problems that Git would have resolved.
As I got assigned work, I noticed I would have to fix more bugs and bad coding, before I could even start the new addition/feature. It was riddled with completely obvious security vulnerabilities that were never fixed. Keep in mind that this was the new product of the entire company with paying customers and real data.
The team lead was also very insecure. I couldn't even nicely mention or suggest fixes in code that he had written. The interesting thing is that he didn't even really have a professional coding background. He went straight from tech support to this job.
I lasted about a year. I got let go due to 'money issues'. Shortly before this, they wanted me to merge my code into my branch with the Jr. developer's code right before my vacation (literally the day before).
I merged it and pushed it up to the repo (as instructed) and the team lead sent me nasty emails throughout my vacation about how various parts of my code 'didn't work'. Not only were these parts the Jrs code, it wasn't ready for production.
The other thing to know about the team lead is that he was extremely passive aggressive and would never give me important project details unless I asked (I'm not talking details, just high-level, what needs to be completed).
We had a call where he told me I 'wasn't a senior developer'. I wanted to tell him to fuck off, but I needed the job. The company went out of business 2 months later.
I found out their entire business model relied only on Facebook Ads, and they got banned for violating their rules.
ahh, there's a lot of scenarios here.
in my scenario, those people were gone.
> In this scenario, I've found that the only productive way forward is to do the best job you can, in your own isolated code, and share loudly and frequently why you're doing things your new different way.
Now you have N+1 ways.
It can work if you manage to get a majority of a team to support your efforts, create good interfaces into the legacy code paths, and most importantly: write meaningful and useful integration tests against that interface.
Michael Feathers wrote a wonderful book about this called, Working Effectively with Legacy Code.
I think what the author is trying to say with consistency is to avoid adding even more paths, layers, and indirection in an already untested and difficult code base.
Work strategically, methodically, and communicate well as you say and it can be a real source of progress with an existing system.
I’ll check out that book, thanks for the reference.
I rarely see large 10m+ LOC codebases with any sort of strong consistency. There are always flavors of implementations and patterns all over the place. Hell, it's common to see some functionality implemented multiple times in different places
And it's fine, right? Honestly I think people need to realize that part of being a good engineer is being able to deal with inconsistency. Maybe submodule A and submodule B do network requests slightly differently but if both ways are reasonable, working, and making the company money, it's probably not worth delaying product improvements in order to make things "more consistent."
On the other hand if no one in your company cares about consistency, at some point everything becomes so awful you basically won't be able to retain engineers or hire new ones, so this is a place where careful judgement is needed.
>and it's fine, right?
The hard part of being an engineer is realizing that sometimes even when something is horribly wrong people may not actually want it fixed. I've seen systems where actual monetary loss was happening but no one wanted it brought to light because "who gets blamed"
That’s always a strong signal to start polishing your resume. Layoffs are probably just around the corner.
That's crazy, is there no opportunity to get credit for preventing monetary loss?
Yeah 100%. Honestly style / technique / language consistency are implementation details, it helps with engineer fungibility and ramp up, but it also works against engineers applying local judgement. This is something to briefly consider when starting new services/features, but definitely not something to optimize for in an existing system.
On the other hand, data and logic consistency can be really important, but you still have to pick your battles because it's all tradeoffs. I've done a lot of work in pricing over the decades, and it tends to be an area where the logic is complex and you need consistency across surfaces owned by many teams, but at the same time it will interact with local features that you don't want to turn pricing libraries/services into god objects as you start bottlenecking all kinds of tangentially related projects. It's a very tricky balance to get right. My general rule of thumb is to anchor on user impact as the first order consideration, developer experience is important as a second order, but many engineers will over-index on things they are deeply familiar with and not be objective in their evaluation of the impact / cost to other teams who pay an interaction cost but are not experts in the domain.
A common experience (mostly in the Pacific North West) I have had is to implement a feature in a straightforward manner that works with minimal code, for some backlog issue. Then I'm told the PR will be looked at.
A couple days later I am told this is not the way to do X. You must do it Y? Why Y? Because of historical battles won and lost why, not because of a specific characteristic. My PR doesn't work with Y and it would be more complicated...like who knows what multiplier of code to make it work. Well that makes it a harder task than your estimate, which is why nobody ever took it up before and was really excited about your low estimate.
How does Y work? Well it works specifically to prevent features like X. How am I supposed to know how to modify Y in a way that satisfies the invisible soft requirements? Someone more senior takes over my ticket, while I'm assigned unit tests. They end up writing a few hundred lines of code for Y2.0 then implement X with a copy paste of a few lines.
I must not be "a good fit". Welcome to the next 6-12 months of not caring about this job at all, while I find another job without my resume starting to look like patchwork.
Challenging people's egos by providing a simpler implementation for something someone says is very hard, has been effective at getting old stagnant issues completed. Unnaturally effective. Of course, those new "right way" features are just as ugly as any existing feature, ensuring the perpetuation of the code complexity. Continually writing themselves into corners they don't want to mess with.
This sounds like you are missing important context. Here is a similar conversation:
"Why do I have to use the system button class. I implemented my own and it works."
"Because when the OS updates with new behavior your button may break or not get new styling and functionality"
"But this works and meets the spec, that's 10x harder"
More like we have to use the god object to make all http calls for consistency in logging, despite this being a gcp pubsub.
Find the context you are missing and if it’s bad reason you will understand their perspective more and be able to persuade them.
Hard for me to comment definitively here since I don't have the other side of the story, but I will say that I have seen teams operating based on all kinds of assumed constraints where we lose sight of the ultimate objective of building systems that serve human needs. I've definintely seen cases where the true cost of technical debt is over-represented due to a lack of trust between between business stakeholders and engineering, and those kind of scenarios could definintely lead to this kind of rote engineering policy detached from reality. Without knowledge of your specific company and team I can't offer any more specific advice other than to say that I think your viewpoint of the big picture sounds reasonable and would resonate in a healthy software company with competent leadership. Your current company may not be that, but rest assured that such companies do exist! Never lose that common sense grounding, as that way madness lies. Good luck in finding a place where your experience and pragmatism is valued and recognized!
I feel your pain. But I guess you need to work harder on detecting these kinds of work places upfront, instead of joining them one after another?
Generally, companies filter out candidates who request to look at any measurable amount of source code as part of the process. Larger companies leveragethe 6-12 mo contractor to hire. You are still stuck there until you are not.
These topics are common knowledge, if you have interviewed in the last 5 to 10 years. I have been working for 25, so I find the blame trying to be redirected, by some, misguided.
Yes, you can't directly look at the source code (unless you pick companies that open source a lot). But I was more thinking of trying to develop some proxy metrics that you can measure; the most common being asking the right questions in the interview. But you can also try to look for other tells.
And to be practical, that's fine. In a big codebase it's more important to encourage consistent, well-defined, small interfaces, and a clean separation of concerns, than to try to get consistency in the lower-level implementation details. Other non-code concerns like coordinating releases and migration of shared services are also way more important than getting everyone to use the same string library.
(Of course, if you carry that principle to the extreme you end up with a lot of black-box networked microservices.)
> but what about when the existing codebase is already inconsistent?
Then you get people together to agree what consistent looks like.
I find the easiest way to do this is to borrow someone else's publicly documented coding conventions e.g. Company ABC.
Then anyone disagreeing isn't disagreeing with you, they're disagreeing with Company ABC, and they (and you) just have to suck it up.
From there on in, you add linting tools, PR checks etc for any new code that comes in.
If there's resistance to picking a style guide, autoformatting might be a viable start and will probably do quite a bit for shallow consistency at the price of large PR:s once per file. Once one has worked with a forced style for a while it starts to feel weird to see breaches of it, and I think that might help softening people to adapting a style guide regarding more subtle things like error handling or attitude to standardised protocols like HTTP.
My approach is what I call defensive programming, with a different meaning than the usual usage of the term. I assume that my coworkers are idiots that aren't going to read my documentation, so I make all public classes and methods etc. as idiot-proof as possible to use. Hasn't saved me from every issue caused by my teammates never reading my docs or asking me questions, but it's definitely prevented several.
> assume that my coworkers are idiots
I know (most?) people don't mean it literally when writing something like this but I still wonder why such self-evident ideas as "make things easy to use correctly and hard to use incorrectly" are framed in terms of "idiots who don't rtfm".
The best documentation is what wasn't written because it (actually!) wasn't needed. On the other hand, even if people aren't "idiots", they still make mistakes and take time to figure out (perhaps by reading tfm) how to do things and complete their tasks, all of which has a cost. Making this easier is a clear benefit.
The bigger the codebase, we all eventually find ourselves in scenarios where we were the idiot. Making the APIs as foolproof as possible, utilizing tools like Semgrep, deprecating stuff in the way your language supports that it shows up in the IDE,… all that stuff should be utilized.
Sometimes people are too afraid of attempting to make it consistent.
I've done several migrations of thing with dozens of unique bespoke usage patterns back to a nice consistent approach.
It sometimes takes a couple straight days of just raw focused code munging, and doesn't always end up being viable, but it's worth a shot for how much better a state it can leave things in.
Highly agree. I've done quite a few large refactors of unnecessarily complex systems that resulted in significant improvement - from lots of bugs to nearly no bugs, incomprehensible code to simple straight forward code, no tests to great test coverage.
I did have one bad experience where I ended up spending way too much time on a project like that, I think I made some mistakes with that one and got in a bit too deep. Luckily my team was very supportive and I was able to finish it and it's a lot better now than it was.
Consistency in huge legacy codebase is like virginity in a brothel: desired but non-existent.
It is terrible to just do this on your own, particularly as the n00b.
If there are 5 different standards in the codebase, don't just invent your own better way of doing things. That is literally the xkcd/Standards problem. Go find one of the people who have worked there the longest and ask which of the 5 existing standards are most modern and should be copied.
And as you get more experience with the codebase you can suggest updates to the best standard and evolve it. The problem is that you then need to own updating that whole standard across the entire codebase. That's the hard part.
If you aren't experienced enough with the codebase to be aggressive about standardization, you shouldn't be creating some little playground of your own.
> If there are 5 different standards in the codebase, don't just invent your own better way of doing things. That is literally the xkcd/Standards problem. Go find one of the people who have worked there the longest and ask which of the 5 existing standards are most modern and should be copied.
I strongly disagree with you and believe you've missed the point of my comment. Think about this: why are there 5 different standards in the codebase, none of which meet your needs? Do you think any engineers on the team are aware of this situation? And how might you get more experience with the codebase without writing code that solves your problems?
“Time” is the answer almost always.
Standards evolve over time, as do the languages and frameworks. Old code is rarely rewritten, so you end up with layers of code like geological strata recording the history of the developer landscape.
There’s a more complicated aspect of “Conway’s law, but over time” that’s hard to explain in a comment. And anyway, Casey Muratori did it better: https://youtu.be/5IUj1EZwpJY?si=hnrKXeknMCe0UPv4
People who created 3 of the 5 no longer work at the company, test coverage is minimal or nonexistent and the system mostly does what it’s supposed to.
In this situation, ‘getting more experience in the code base’ is more or less synonymous with ‘getting paged on the weekend’.
> Do you think any engineers on the team are aware of this situation?
Yes, there probably are. If you haven't been working there for long enough to know who they are, then you shouldn't be YOLO'ing it.
The fact that it hasn't all been cleaned up yet is due to that being an order of magnitude harder than greenfielding your own standard. That doesn't mean that nobody is aware of it, or working on it.
I've absolutely worked for a decade on a codebase which had at least 5 different standards, and I was the one responsible for cleaning it all up, and we were understaffed so I could never finish it, but I could absolutely point you at the standard that I wanted you to follow. It also probably was somewhat deficient, but it was better than the other 4. It evolved over time, but we tried to clean it all up as we went along. Trying to ram another standard into the codebase without talking it over with me, was guaranteed to piss me off.
Consistency is never the sole reason to change something, there's always consistency and at least one of the following:
- coherency
- type safety (shout-out to dynamically typed languages that adapted static typing)
- concurrency
- simplicity
- (and more)
> If it's actually better, others will start following your lead.
A lot of people don't want to improve the quality in their output and for various reasons... some are happy to have something "to pay the bills", some don't want to use a programming language to its full extend, some have a deeply rooted paradigm that worked for 10 years already ("static types won't change that"), others are scared of concurrency etc. For some people there's nothing to worry about when a server can be blocked by a single request for 60 secs.
I advise against this if you have not been allocated the time or budget to revise the code. For one thing, you're lying. For another thing, were you hired to be a part of the contributing team or hired to be part of a research team doing experiments in the contributing team's codebase and possibly deploying your experiment on their production systems?> your work is an experiment and you will later revise it
I would immediately push back on any new guy who says this, no matter how confident he seems that his way is the right way.
Counter-thought:
We are making brand new things here and not being in an assembly line coming up with the very same thing dozens to million times. We are paid to make new products never existed, having novelty elements in it desired to be a bigger extent than not!
Those pretending knowing exactly what they are doing are lying!
Of course we are speculating here about the size of novelty content to a differing extent, which is never 0% and never 100%, but something inbetween. But those pushing back on those at least trying to revise the work - putting emphasis on it -, deserve no-one coming to them to be pushed back (at least for the inability of allocating resources for this essential activity of development. Development!).
(tried to mimic the atmosphere of the message, sorry if failed)
> but what about when the existing codebase is already inconsistent
It depends.
If it's a self contained code base, automatic refactoring can safely and mechanically update code to be consistent (naming, and in some cases structurally).
If it's not self contained and you're shipping libraries, i.e. things depend on your code base, then it's more tricky, but not impossible.
"Lean on tooling" is the first thing you should do.
To me the (a), (b) and (c) tactics remind me of people who were very hard to work with. I believe a better approach is indeed like you already mentioned, explain and document, but, as an extra: also be open to comments on the docs and implementation. Often there's a reason that your particular approach was not used earlier, like explained in the article.
My last experience with this, in the section of code I had to navigate for my first ticket at startup X we had some code that was querying the same tables multiple times unnecessarily. We were also using a highly bespoke (imo) code library with a relatively small following on github but this library permeated the entire codebase and dictated the way things had to be done. I tried to just make my changes by touching the code as little as possible, but avoiding the most outstanding inefficiencies. I thought of my changes as a small oasis of sanity in a desert of madness. In that first PR the seniors tore me a new one. It turned out there was a v2 of "the right way of doing things" that I had to learn about and then write my code to conform to. v2 had it's own issues, though was perhaps not as bad as v1. Later on when I became more influential I was able to successfully advocate to management to change many things to my liking, including axing the beloved exotic library that distinguished our codebase. But the old guard remained highly resistant to changing the way our code was written, and switched their stance from 'this is great' to, 'it sucks but its too much effort not to keep with it'. I am left feeling that it was all not worth it, not just the struggle but whether the product was appreciably effected one way or another. Just another crappy war story of my blighted career.
This resonates hard. In particular, managing the old guard’s emotions is a defeating process. It is almost always easier to jump into a project and critique it than it is to start from scratch. New perspectives and better ideas should be welcome, but instead they can be shut down because folks take things personally.
My (rather unfortunate) conclusion is that when I encounter this behavior I move to another team to avoid it. If that’s not possible it’s honestly worth looking for another job.
I had this in 2018 with a company that were still using csh scripts in all of the development tooling.
Relevant xkcd: https://xkcd.com/927/
- [deleted]
"Now we have five inconsistent coding conventions..."