I have some shitty code that runs an important part of one of my SaaS businesses. I'd love to refactor it. I'd love to swap out the HTTP library that it uses with the better one that the rest of the project uses. I'd love to remove all the nesting and complexity. I'd love to make it <i>not</i> an eyesore.<p>But here's the problem:<p>- In the past five years, it has never failed.<p>- In the past five years, it has not needed to adapt.<p>- If I change it successfully, it will make exactly zero difference to my paying customers.<p>- If I change it unsuccessfully, it will break a core part of the system that people are paying for.<p>If I were to refactor it at this point, it's really only to pander to my own ego, which <i>probably</i> isn't a sensible business goal.
> I know how to write good code.<p>> Usually it means spending more time now to make the code better for some future state of the world.<p>No. That is called premature optimization/abstraction/development and should be avoided. Shitty code is shitty today. Poorly chosen names, copy-pasted pieces, implementation doesn't reveal intent, mismatch in code and comments, etc. Doing these right should be habitual and shouldn't be a function of cycle time. Otherwise, they tend to creep easily.
Nope.<p>The startup I work at has been compartmentalizing and chipping away at our shitty code for the past eighteen months. We're not doing this because it offends our aesthetic sensibilities. The shitty part of our codebase is truly shitty. It fails to do its job regularly, causing significant toil in various parts of the business. Junior engineers can't make simple changes to it without causing serious regressions in seemingly unrelated parts of the system. It's slow as fuck. New engineers have to spend weeks reading the codebase if they hope to understand it, and some just give up. There's a big, measurable, drop in velocity whenever we have to work with the shitty codebase. So now we're effectively forced to do an incremental rewrite in order to build new features, which is terrible because rewrites suck and bring their own drop in velocity with them.<p>In practice, writing shitty code is not all that much faster than writing good code <i>if you know how to write good code quickly</i> - but I guess some people don't bother to learn.<p>I read somewhere once that at a growing company the next person who works on the code you wrote is highly likely to be new to the company and have less industry experience than you. When you write code, you should write so that that person can work on it productively.
Better: Learn to write non-over-engineered code that is obviously written by a professional without thinking.<p>Even better: Learn to write boring code, fast.<p>There will be exceptions, but those are rare and better resist the urge for abstraction until the very last moment!
That's not "shitty code", just "simple code". Also regarding the last paragraph, IME the more senior you get the more you realize that writing code for hypothetical future requirements doesn't make much sense. I've seen this sort of enthusiastic overengineering much more often with junior peeps fresh out of university (including my younger self).<p>PS: It also seems the author is still early in the learning process, my advice: what you describe are all good programming practices, they're not just useful for the "learning phase", keep them!
This present a false dichotomy of extremes: shitty code and hopelessly overly complicated intended to address all manner of eventualities that never materialize.<p>Both extremes are awful choices IMO. If the code you are writing is truly unlikely to be extended and adapted then it may be fair to relax your requirements for code quality. As for most things I'd say that applying some discretion to your actual context is more valuable than blanket approaches of embracing bad code.<p>Code can be good without taking forever and without being over-designed. On the other hand the accumulated effects of shitty code upon more shitty code is unlikely to be a net positive.<p>Well-designed code can be simple.
There is a huge difference between planning for the future (which you should not do, agreed, unless that future is < 3-4 tasks time, say) and writing shitty code. I class shitty code as not what he's said but things like works by accident, no tests, naming is misleading, 3000 line files, spaghetti
code etc. etc. Doing these things does not make you faster or produce more software.<p>My opinion is "do the thing that another developer can understand quickly" is the minimum requirement. Even if the code doesn't work (or has a bug!) at least it has some chance that it can be fixed.<p>I think the author is conflating two issues, 1) planning for the unknown is impossible and time consuming (true!) and 2) all software engineering practices also make coding slow (false!).
So instead of <i>prototype often, go production early and iterate frequently</i> is now spelled: <i>Write shitty code</i>?<p>Oh boy...<p>If masters and geniuses of the past would read the "common-sensical" advice we hear today and how it is being expressed, they would not be surprised by the general shitty state of the world (all civilisations around the world) but to find it plain miraculous that we're still in a semi-funcional way or alive at all.<p>If you know how to write good code, write good code for the state of the world right now then. Care about the future in the future. Kent Beck taught us this already in a very detailed way two decades ago. Invented Unit Tests in Smalltalk to have fast feedback on regressions frequently and granularly. All languages have that today. Then why in the world do, instead, these software engineer self-serving rationalizations that will accumulate tech debt you're carefully not talking about?<p>How in the world an unexperienced developer would receive this advice as a propaganda in any way other than a push of self-indulgence for incompetence? How this advice makes you an <i>A player</i>?
Coding is an important skill to develop. But I'd rather develop my ability to NOT write code.<p>To choose libraries I won't have to mess with. To DRY up the architecture so I'm not implementing the same thing 5 times in a higher layer that would be trivial in the lower layer.<p>To not encourage sprawling piles of code by making sure there's one obvious way to to the things that need doing.<p>Shitty code will just teach me to code. But I already know how to code. I've been coding for a while now.<p>I don't need to be any better at writing average crappy code. I need to be better at writing bulletproof code that is as close to bug free as it can get, and architecting to prevent the need for shitty code.<p>Should musicians be lazy in their practice? Everyone outside of tech says you will play how you practice.<p>So if they encourage shitty code... you wonder if they really are all that committed to making bug free apps, or if they are dragging the old WorseIsBetter anchor.
Great code takes a while. I've learned to take this approach:<p>Works -> Like -> Love<p>First I write code that works, pretty much by whatever means I can. Sometimes it's great, but most often, shitty is the right word for it. But it works (sometimes, well, sometimes... um...). Then I try to refactor to something that is likable. It's often not perfect at this point because maybe it's not as performant, there are side effects or the api isn't as well designed as it should be. The final step is to really go for something that is lovable, which usually means will not have to be re-written for a while, and often becomes quite re-usable. The nice thing about this approach is that I'm able to get things working quickly (and make customers happy) and it differs optimization until enough is actually understood to optimize.
Not convinced. Unmaintainable code is the big problem. He is right that the future is unforseeable. That's why code needs to he maintained. A weird non-local bug is detected. The developer who wrote it found greener meadows. Sales has sold a feature we don't have yet. We notice that we do business with some different functionality that we originally envisioned. There will always be reasons why code needs to be maintained, whether it has worked for 3 days or 3 years.<p>Now if the code is unmaintainable and every change takes 5 times as long as soon it should and introduces 3 new bugs we are in trouble.<p>Shitty code is almost always unmaintainable. (Unless the shittyness is local inside small functions, but I doubt that's acommon case.) Of course over-engineering and needless optimizations also lead to unmaintainable code.<p>If the company policy were "Write shitty code" I would run away. I am sure it would lead to absolutely horrible results. At my work we stress the value of good code. Still we end up with shitty code regularly. We just didn't know better when we did it. And we do not rewrite everything once we believe to know better. It's hard enough to struggle with that level of shittyness, I don't want to have it increased fully intentionally. I do that for scripts I will never use again, but not for anything I merge to the code base.
I recently made a similar observation: Write shitty!<p>Background: I am writing a novel. In the beginning I sat down and obsessed with every little detail, spending multiple days figuring out the perfect formulation and text flow. And then I would maybe change a little thing in the story-line and have to start over again. This really sucked the whole fun out of the writing process.<p>Then I changed my approach: I just write whatever comes to my mind, independent of whatever I just wrote before. Doesn't matter if the previous scene had character A as sidekick, if I like character B more, then I simply switch them. Some scene is way over the top and doesn't make sense? Doesn't matter, I write it. Writing is fun again. I sit down, get into deep-work-mode and just write. No constraints, no thinking.<p>Granted, this is only draft #0 and I will probably have a rude awakening when I have to merge all my writings for the next iteration of drafs drafts, but so far I am rolling!
This is expressing something I've been trying to express for _years_. I've come to call it "throw away code" instead of "shitty code" because of what's happening in the comments: people always came back to me and said "Shitty code doesn't work!" and we get into an argument about what I mean by shitty code.<p>I find that throw-away code gets my point across much more cleanly without people's objections being raised immediately. Most engineers understand that when I say "throw-away code" it doesn't mean the code doesn't work. It just means that I can _throw it away_ when I need to later. It is "shitty code" because it's _supposed_ to be replaced.
There’s a time and space for shitty code. Something you write once, as quickly as possible and throw away.<p>Anything else, that ships to any kind of production environment, needs to be reliable and maintainable.<p>Shitty code is a false economy. Time savings often fail to materialize if not out right end up costing more in maintenance.<p>Good code is not something extravagant nor expensive. It’s something simple that just works and looks obvious to anyone who looks at it.<p>Good code is not over designed code.<p>The difference between shitty code and good code is often the difference between a prototype and something production ready.<p>Take the prototype code you wrote, careful think of all the edge cases, maybe test them, rename anything confusing, comment non obvious things if any, and refactor, throw away as much as you can until every line matters.<p>Good code does not trouble your sleep because you know you did everything you could to ensure it is reliable and even if there is a bug, there’ll be enough info logged and the code is so clear, that fixing the issue will be obvious.<p>If you’re in the habit of writing good code, you will become better at it with practice and be extremely efficient. If you’re in the habit of writing shitty code, you’ll have to write more shitty code to work around shitty bugs and spend endless time chasing mysterious problems in production.
I think intentionally writing shitty code is not a good advice, but it does help with taking us out of the rut of perfectionism and over-engineering stuff!<p>For me I try to follow two principles:<p>First, write boring code, and I mean boring, long variable names, no syntax sugar, no clever combination of boolean chains... code that I would use to teach a beginner in the language (or more aptly, future me) how to start coding!<p>The second is plan to write things twice. This one is a double-edged sword inasmuch as it can help to further clarify or make even more boring the code, or be the place to start adding "optimization" and "cleverness" in the code... I try to stick to the former.<p>Some of the most valuable code I've written I would call either "hacks" or "scripts", not code, but nonetheless, it solved a real problem instead of being beautiful/elegant or whatever
My impression is a lot of the best practices spread today are suited best for FAANGs or at least high tier SaaS businesses (which often contribute to the frameworks used).<p>One thing I hear repeated over and over is: Code is going to change frequently. Well that's not universally true. I don't even think that's true for the vast majority of code in production globally.<p>If you build an app with one specific, well-defined purpose that iself is not going to change a lot (eq. supporting the workflow of a specific business, managing inventory, etc.) that might easily run for a decade with <10% LOCs ever being touched. I've build dozens of those in my career and I'm certain I'm not the only one.<p>If you're on the quest for the most beautiful code in this scenario you're wasting your client's time and money for your own vanity.
I agree with the author. Learning to write good code, and well designed systems, requires writing lots of code. The best way to do that is just pump it out. It’s harder to write a lot of code if you try to make it perfect and cover a lot of future scenarios.<p>The more code you write, the more you start to see larger patterns that hinder or improve your system design. You don’t see those things as easily if you are writing bits and pieces slowly. As an aside, writing code in a professional setting can be limiting as well since you may not be able to write code quickly (or shitty) enough to get to a place where you see the effects of your decisions. You’ll write less code and end up focusing on making safe decisions, which means less experimentation in techniques.
If you work on your own, that's probably not a disaster to write shitty code. BUt in a team it's a big no for me. And it's not really about the future extension of code, although modular approach generally helps here too. The biggest benefit of non-shitty code is that others can get through it quicker. In 99% of cases code is written to be read by others in the first place (including yourself after some time), then goes the bug-less priority and then anything else. 1% is where you really need something to be performant - the ease of reading is then not important at all as such code has to be messy.
Meh, not convinced. Sure you can craft a shitty built bike with cheap parts so to learn more quickly about how the bike fails, and sure you can probably make some money selling these shitty bikes.<p>But I’m in it for the craft. I want to build and sell super high quality bicycles that ride and look beautiful and will ride for a lifetime.<p>And for that, every little detail of the bike has to be crafted in a high quality manner.
I've learned to embrace the jank. Architecture matters, but implementation rarely does. You can sit around all day dreaming up a perfect parsing library with full test coverage and a pure functional API to do some string manipulation, or you can smash out a 20 line function that works for your case and move on with your life to things that matter.
The problem is, future programmers only get to look at the artifact--your shitty code--rather than all the learnings you got, since I bet you never wrote it down.<p>Oh right, I'd have to ask you about it. And chances are, you're not even working here anymore. <a href="https://news.ycombinator.com/item?id=29919428" rel="nofollow">https://news.ycombinator.com/item?id=29919428</a><p>That said, there are definitely people that swing too far the other way. Some people create abstractions where there doesn't need to be. And they're usually not well thought out ones either.<p>I wish there was a way for code work like pen/pencil and paper, where if you're doing exploratory work, you can sketch. And it'll look like a sketch for other people reading your code, however that can be indicated. But once you learned enough of the domain, you can firm things up, like you do with a pen to add safety and other constraints.
Most of the previous comments seem to be firmly in the 'nope' camp when it comes to code quality. What most of the appear to be missing though is that they're looking at their production codebases, and in that instance, I would wholeheartedly agree - Production code (or code destined for Production) should be of high quality, developed to standards and over-engineered where applicable.<p>Where I think the OP is referring to writing shitty code is when coding either purely for the fun of it ... to learn a new technique, or a new language for instance, or when prototyping something - sending out the feelers to see if something you can hack together in a short space of time could be developed into a viable solution (at which point, you'd then start to either refactor it into decent, production ready code, or toss it aside and begin again).<p>Shitty code definitely does have its place, but anywhere near a production codebase isn't it.
I think this is OK in some situations and not OK in others.<p>To me it is a lot like writing in a human language. When I am texting someone I am close with there is no reason to avoid slang or to use the best grammar and punctuation. When I am writing my CV it would be advisable to be more formal.<p>The author of a dime-store novel will write at a different standard than the Encyclopedia Britannica.<p>My point is that you should write the code that is necessary for the situation and if you are part of an engineering organization the leadership or team as a whole should decide when to use what level of quality and should understand what the trade offs are. There are legit business reasons to write throwaway code when time is of the essence and there are times when you need to take the time to risk over-engineering/over-building something just to be as certain as possible that it won't fail.
My usual approach is a bit like how I wrote essays on loose-leaf paper back in school: Start with a rough draft, and then once all your edits are done, produce a "clean" copy.<p>I think a lot of code that is described as shitty comes from doing the first step, but due to time constraints the second step is skipped.
This is one of those posts that exclusively caters for reassuring the audience regardless of whether they are doing right or not just to make them feel better.<p>Refactoring is part of a cycle and you do it before you publish your code to others. If you don't clean it up, you are wasting someone's time.
I think there's a balance to be had. I used to write shitty(er) code, and often when I have to revisit it to mod for a slightly different purposes my thought is something like "that lazy ass, I'm going to have to nearly start from scratch to make it work for what I need now"<p>So now, unless time crunch is so bad I have no option (and even then I push back against it to try & get more time to do it right) those are the time I'll cut corners on robustness, readability, etc. I also put a comment in at the top with a deep apology to the angry version of my future self with a reminder if the circumstances at the time. It helps me not pound the keyboard so much in frustration.
Perhaps I don't subscribe writing "shitty code" on purpose, but there is something to be said about having a code playground where you can explore ideas, API and languages with little care about practices, design or having concrete goals.
I believe most shitty code is not a result of unfamiliar tech stack, tools or environment but not enough time spent on thinking about the problem at hand and how the solution can be open to modification. In that vein, the author encourages shitty code.
Ironically, for an article titled "Write Shitty Code", the entire article is blank for me because they wrote shitty CSS.<p><a href="https://imgur.com/a/41eXasz" rel="nofollow">https://imgur.com/a/41eXasz</a>
Anyone that did not write shitty code didn't do a lot of programming. The author picked a great example here.<p>You should feel good when you know what mistakes you did because that means you know better now. This process should be iterated a few times.
This is a false dichotomy that I see in a lot of developers(including myself a while ago): fast+ugly vs slow+futureproof.<p>In a big codebase futureproof is more important, but let's put that aside and talk about quick and dirty standalone programs. Even when talking about the needs of now, ugly code can slow you down significantly. Ugly code takes you longer to debug, longer to see errors, longer to modify, longer to built upon even in the next hour.<p>So for me it's a matter of writing code that is at least good enough for me NOW, and then I start thinking about futureproof and other maintainers as I get closer to a commit and during pull requests.
Slightly tangent but one thing that these code practices articles often miss is using the language to its fullest. Most beginner developers who are not very used to the language reinvent the wheel for a lot of in-built functions. For example, I've seen stuff like:<p>`
const arr = [];
for (let i = 0; i < anotherArray.length; i++){
arr.push(someOperationOnEachElement(anotherArray[i]))
}
`<p>instead of<p>`
const arr = anotherArray.map(someOperationOnEachElement)
`<p>Not knowing what your tool/language can lead to hard-to-maintain code. One should always try to leverage the expressiveness and <i>syntactic sugar</i> wherever relevant.
Slightly tangent but one thing that these code practices languages often miss is using the language to its fullest. Most beginner developers who are not very used to the language reinvent the wheel for a lot of in-built functions. For example, I've seen stuff like:<p>`
const arr = [];
for (let i = 0; i < anotherArray.length; i++){
arr.push(someOperationOnEachElement(anotherArray[i]))
}
`<p>instead of<p>`const arr = anotherArray.map(someOperationOnEachElement)`<p>Not knowing what your tool/PL can do can lead to hard-to-maintain code. One should really leverage the expressiveness and syntactic sugar wherever relevant.
I think developers, especially those with, say, under 15 years of experience, place far too much emphasis on code. No doubt, <i>very</i> shitty code can have a big adverse effect on long-term maintainability, but correctness is much more influenced by algorithm design, which is often better and more quickly explored in something like TLA+ or Alloy than in code. A bad algorithm design costs much more than ugly code. Focusing on code aesthetics also takes time away from other, more important, activities, such as profiling.
The shitty code/do it supposedly once is quite widespread in academia, and from what I have heard also in at least some biotech companies.<p>The problem:
irreproducible results.<p>Once you chain dozens of command lines, unversioned shitty scripts, no md5 checksum-ed inputs/intermediate results etc. you are in a smelly swamp full of monsters.
Even you one week later will not be able to do it again.<p>Markdown docs/jupyter notebooks are minimum even if one does quick and dirty proof of concept thingy IMHO.
> A lot of senior experienced people will talk about all the “shoulds” your code should be like. They are usually describing some far distance promised land that they have never visited either.<p>Oh how I can relate do this. I've seen so much guilt and inaction from developers who are paralyzed by "should". They so badly don't want to be a "bad engineer" that they'll over-engineer just about everything and end up causing a lot of problems.
Valid arguments but this is a terrible advice.<p>Good or bad code is a learned ascept of an engineer, or experience rather. If you care enough then eventually you will write great code unconsciously with high velocity. Someone's shitty code might be fucking beautiful if they practiced for many years.<p>My advice is, work in a place with good code review, code quality culture because it really does make the difference. We're unconsciously shaped by our peers.
I get it, but as a senior member of the team you have to be careful. Yes it is fun to get shit done really fast, yes it creates short term value but you have to be aware of the downstream effects. When you build new stuff, set patterns, create infra, you are setting the example that junior members will learn from at best, and copy/paste at worst. Do you want a ball of mud because its all too easy to get one
Ok. there's shitty, and there's _shitty_.<p>I hope that screenshot is a joke, cause holy crap! That doesn't look good.<p>I seriously wish the author is not defending that kind of shittiness.<p>I am all good with the if(process.env.env !== 'notProd') part, but indentation, come on?<p>I think this can't be a real piece of code anyways, cause the author would have done console.log('here1'), console.log('here2'), and console.log('here3').
The valid point is: "Reconnect with the joy of creation"<p>Attempts to do everything absolutely correct are not productive on early stages.<p>So it's OK to write dirty during search for solution. Anyway it's my personal code on this stage.<p>But then comes time to publish code. I respect my colleagues, I can't make them read dirty code.
> <i>shitty code</i><p>Still, I have never understood why anyone would <i>want</i> to write<p><pre><code> if (success == true)
</code></pre>
instead of<p><pre><code> if (success)</code></pre>
This article makes some very good points, all of which are ultimately false.<p>To make a medical analogy, I don't know that I would trust a surgeon with a billion operations record and a 50% complication rate more than one with a 1000 operation record and 1% complication rate.<p>And I certainly would hate to be the colleague inheriting their patient.
Totally agree with everything in the article. Unclear how this is different from prototyping code before you start on the industrial-strength version.
<a href="https://en.wikipedia.org/wiki/Software_prototyping" rel="nofollow">https://en.wikipedia.org/wiki/Software_prototyping</a>
I love when a project transitions from clean to shitty code (which eventually happens as quick fixes and shortcuts pile up). At that point I can start iterating much faster since I no longer care about making everything as clean and smart as possible. The battle is already lost so I can just embrace it.
Shitty code writers typically justify that it's some(thing/body) else that doesn't allow them to write OK code. They trade the imaginary speed gains (my estimate it's 10-20% of effort difference between shitty and OK work) for problems that start manifesting immediately or are ticking bug/maintenance bombs.<p>The problem is that a lot of engineers try to go the shortest way possible, instead of investing extra effort to think little about the future. They see it either black or white, either shitty or perfect - there is no middle ground. I just noticed that every type of copy/pasting is now ok if your application is using "microservices" architecture. Nobody knows why "microservices" were chosen (as there is no demand for it, nor the implementation of that "architecture" allows them to easily scale), but hell, now we don't need to isolate the common stuff into a single place. And then, when they see some crap, then they keep adding more crap for a sake of consistency.
For me some top qualities of good code is -
Readable
Maintainable
Fault tolerant<p>If it works, there are no plans to extend it and there is no one to read it - then it doesn't matter what it is. I know its good code without having to look
> A lot of senior experienced people will talk about all the “shoulds” your code should be like.<p>And the article itself is another "should".
That code snippet is like one of those Lovecraftian paintings where it looks normal, but the more you look at it, the more weird and horrifying it gets.<p>Bravo.
the best way to write code is: write code that you can delete without any effects on the rest of the program. and keep all related code as close as possible together in the source code.
Also keep mutations as far up the call stack as possible. use pure functions whereever possible.
My interpretation of the actual post is less, write “bad” code, but more don’t let code quality prevent you from trying something. The former presentation of it though I can’t agree with while the latter I do.<p>I feel code quality is important, not because of future state and optimization but a strong architecture will facilitate other engineers learning, which is critical. For example, not all engineers have learned design patterns like the strategy design pattern or observer, etc (see <a href="https://refactoring.guru/design-patterns/catalog" rel="nofollow">https://refactoring.guru/design-patterns/catalog</a> for great examples) where a little guidance can save them a lot of pain.<p>With that said, I understand we all bring different skills to the table. Some people love making every linter pass while others could care less and some actively hate it. This is fine, we just need to respect each other enough to try to build on each other’s strengths and support each other where we’re weaker.<p>TLDR: Rather than saying it’s fine to write “bad” code, say don’t let code quality intimidate you, and be willing to try. You can always refactor later and you’ll learn as you go.
The software priests who taught the MSCS courses I took did not teach these insights. No harm comes from studying the beautiful code of the ancients. But, indeed, less work gets done when you complicate your task with goals of beauty and future-proofing.