16 ms·
Code doesn’t have to be a mess
- dsieger 4y agoWould be curious to know what strategies other people apply in order to keep complexity down over time!
- idontwantthis 4y agoUnit Tests. If you can't write a unit test for it, it's too complicated and it's going to snowball quickly into a giant mess.
- marginalia_nu 4y agoUnit tests, while good at promoting decoupling, can absolutely be a major driver of complexity, as it may break the code into far more units than what is reasonable.
- bbarn 4y agoI love unit tests, but admit I have absolutely seen unnecessary complexity including complete classes and namespaces solely to enable testability in many cases. It's a justifiable trade off for me, but I don't pretend that unit testing reduces complexity.
- nosianu 4y agoI think it is of at least slight interest to some who missed it, to bring back this thread from 2018, about Oracle code (I too once worked on it so I immediately saved that comment link when it was posted): https://news.ycombinator.com/item?id=18442941 https://news.ycombinator.com/item?id=18442941
- idontwantthis 4y agoI'm not sure if you're saying so, but those are not unit tests.
- nosianu 4y agoYes it is about tests in general. I think it fits the discussion and many comments very well, this does not really seem to be about only unit tests specifically. Many comments are more general in tone. The very comment at the top of this sub-thread does not seem to limit itself to the subject of unit tests.
- matheusmoreira 4y agoMy experience with automated testing was great until I had to test I/O functionality: files, databases. That's when the test suite itself became too complicated.
- dasil003 4y agoBe careful with this. Unit tests don't tell you much about the correctness of a system overall, and they rarely survive a substantial refactoring. Optimizing for unit testability can make individual classes/functions "simple" but at the expense of creating a ton of them and pushing the complexity to the interfaces and integration between them.
- deleted 4y ago[deleted]
- dsieger 4y agoAbsolutely! For me, comprehensive testing is key to keep things clean over time. Not sure why this didn't come to my mind when writing the article. I think I was somehow assuming that this is a necessary pre-condition anyway.
- adam_arthur 4y agoSingle source of truth is prob the biggest offender I see. Same conceptual state gets represented in multiple variables or derived variables, and these must stay in sync. Very brittle
- bbarn 4y agoTrust your tooling, and your repository. It's safe to delete if you still have a record of the way the code was before. Too often I see code that doesn't need to exist because someone is afraid to remove it. Modern IDEs are excellent at showing dependent code, and GIT and other source control tools are excellent at giving you freedom to remove things. Oh, and have good testing in place to make sure you aren't breaking a required path that your IDE can't detect, obviously. No IDE in the world can detect "Oh, we still had one client on that old obsolete REST call and they are pissed"
- foobarian 4y agoThat's what we call 'scream testing'
- mystickphoenix 4y agoFor me the number one thing I try to focus on is _naming_. If something is hard to name, it's likely hard to understand or overly abstracted (misdirected). If something is easy to name, it likely follows [insert any software development "best practice" here]. What's a good name? I love the phrasing from _Elements of Clojure_ by Zachary Tellman [1] > Names should be narrow and consistent. A *narrow* name clearly excludes things it cannot represent. A *consistent* name is easily understood by someone familiar with the surrounding code, the problem domain, and the broader [language] ecosystem. 1. https://leanpub.com/elementsofclojure/read_sample https://leanpub.com/elementsofclojure/read_sample
- bcrosby95 4y agoYeah, I find that if you can name something well then everything else falls in place much easier. At work, for any large feature, we usually go over naming pretty extensively, and aim to be consistent in documentation, code, and discussions, so everyone knows exactly what everyone is talking about.
- nicoburns 4y agoI'm a big fan of the "IO Sandwich". This is where you keep complex computation as pure functions as much as possible. And push the IO to the edges of the system. So you might have read-compute-write. This keeps the computation functions testable and composable.
- eyelidlessness 4y agoIn probably my favorite software-related talk[1] (certainly the one I most frequently share), this is referenced as “functional core, imperative shell”. 1: https://www.destroyallsoftware.com/talks/boundaries https://www.destroyallsoftware.com/talks/boundaries
- b3morales 4y agoDoes anyone know of a transcript of this talk? There is a link on the YouTube copy of the video, but it seems to be dead.
- eyelidlessness 4y agoThank you for asking. I regret posting this without looking for a transcript first, especially since my capacity for consuming video/audio content has declined as rapidly as a lot of topics I’d be interested in have embraced video. I may well contribute to transcribing it if I find some free cycles.
- Chris_Newton 4y agoYes, this is the way. In addition, often the internal and external representations of information will be different, in which case I normally prefer to keep any conversion or validation logic as close to the corresponding I/O as possible. Then all the internal computation logic only has to work with a clean and well-defined internal data model.
- jerf 4y agoI like a lot of your other replies. I also have a philosophy of doing net improvement every time I go in. If you put a little bit of elbow grease in every time, the net effect on your code over months is pretty nice. But you also have to understand and internalize that it's OK to do a little bit of improvement each time. You don't have to go in, pick up a piece of code, sigh dramatically, and fix everything you can see about it. Just fix a bit. Turn some strings into enumerations or a custom type. Turn a recurring series of arguments into a single struct. Rename a deceptively-name parameter or function variable into something correct and meaningful. Add a test case for what you just did, or add a test case for something even related to what you just did that was not previously covered. Even just one of those is a good thing. Don't give in to the temptation to throw a 15th parameter on to a function and add another crappy if statement in to the pile of the god function. Don't fix the god function all at once, just take a bit back out of it. If every interaction on the code base is net positive, even just a bit, over time it does slowly get nicer, and if you greenfield something with this attitude, it tends to stay pretty nice. Not necessarily pristine. Not necessarily nice in every last corner. But pretty nice. And if you do need to take out some technical debt, you'll have the metaphorical capital with which to do it; a non-trivial part of the reason why technical debt has such a bad rap is that it is taken out on code bases already bereft of technical capital, which means you're on the really bad part of the compounding costs curve to start with.
- plainnoodles 4y agoI'm not a greybeard by any stretch, but I personally get a lot of mileage out of just stopping to ask: Does the extra layer of abstraction, or extraction of code to a method, or creation of a class - does it make the code *right now* easier to understand? If yes, do it, if not, don't. The example I keep coming back to is when I was a junior, one of the other juniors refactored the database handling code in one of our apps to use a class hierarchy. "AbstractDatabaseConnection" "DatabaseConnection" etc. And mind you this was on top of the java.sql abstractions already present. I don't necessarily know what his end goal was, since the code still seemed pretty tightly coupled to how java and postgres handle connections and do SQL. One might theoretically now be able to create a testing dummy connection that responds to sql calls and returns pre-baked data. But the functions we had were already refactored to be pure functions, and the IO was just IO with no business logic. Anyway, all it ended up doing was making it so I never touched the database code in that app ever again. Integration testing was handled by just hooking it up to a test db via cli args and auto-clicking the UI. And eventually when people started side-stepping it, I took the opportunity (years later) to just go back in and replace both it and all the side-stepped code with plain ole java.sql stuff that literally anyone with two thumbs and 6 months of java experience could understand. So now, unless I have some really strong plan (usually backed up with a prototype I used to plan out the abstraction) for an abstraction model, I just write code, extracting things where the small-scale abstractions improve current readability, and wait for bigger patterns (and business needs) to emerge before trying to clamp down on things with big prescriptive abstraction models.
- nicwolff 4y agoI call this "copy-paste-copy-paste-refactor": don't factor or abstract out a routine before the third time it's implemented. Until then you don't know what the actual commonalities among the uses will be, or if the callers will have so many special cases that the routine isn't really that reusable.
- nicwolff 4y agoPrioritize functional testing over unit testing, which penalizes refactoring.
- deleted 4y ago[deleted]
- aahortwwy 4y agoAll dependencies should be injected (and possibly wrapped with custom interfaces, if they're libraries). All globals should be configurable (most codebases I've seen have a ton of hidden globals). All side effects should be isolated. "Break any of these rules sooner than say anything outright barbarous."
- rbongers 4y agoThere's a great section in The Practice of Programming where the book describes how you should structure your code to not just be structured nicely now, but to plan for the future; to structure it so that changes are easy, organized, and don't break anything. It's not exhaustive but it's a powerful general idea and I always like introducing developers to it for the first time.
- deleted 4y ago[deleted]
- commonlisper 4y agoI must say here that I agree this point but one should also exercise caution that they don't go overboard with abstractions while planning for future. Abstractions for future planning should be lean and flexible enough to be extensible.
- jewel 4y agoSometimes called YAGNI, or You Ain't Gonna Need It. I've found that the right level of abstraction is the one that saves time and effort and duplication now, for the features you're currently shipping. If you're thinking about hypothetical new features that aren't even on the roadmap then you've gone too far. As an example, say your embedded program needs to load images, and your standard library only supports raw BMP files. BMP images are going to work great for the time being, since the art team can supply them that way. By all means, add abstraction method around the library method called "load_image" so that you don't have to refactor a million places to replace that library. It'll give you a great place to add error handling, logging, etc. Beyond a single method to add a point of attack, don't go beyond that and spend the time to add a JPEG and PNG library, don't add support for high bit-depth images, or for grayscale images, or CMYK images. Don't add an abstraction that'll someday be able to load the Nth frame from a video file. Perhaps throw an exception for unusual input, but beyond that don't waste your time. In my experience having simple, direct code makes it easier to refactor in the future when the need arises. Having too much abstraction or future proofing gets in the way because the future inevitably will bring different changes than what you were expecting.
- ape4 4y agoAh the Unix philosophy. `man ssh' gives `ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-J destination] [-L address] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] destination [command]`
- civilized 4y agoWorks quite well in conjunction with Googling "how do I do X in ssh stackoverflow".
- ape4 4y agoYou can do X. X Window forwarding ;)
- cassianoleal 4y agoDid you mean `ssh --help`? `man ssh` gives me detailed descriptions of all flags.
- dannyobrien 4y agoWasn't the "Unix philosophy" explicitly formulated by Rob Kernighan in 1983 in opposition to this kind of growth? I mean, there's a whole website of Unix purists named after it: 'UNIX Style, or cat -v Considered Harmful' http://harmful.cat-v.org/cat-v/ http://harmful.cat-v.org/cat-v/
- morelisp 4y ago> Rob Kernighan Is this a typo or a proposed Bourbakism?
- mananaysiempre 4y agoAlso, however convenient or well-implemented it is, SSHv2 the protocol itself is very much an all-singing, all-dancing monolith that’s pretty much doomed to have an Implementation Of Unusual Size. The Plan 9 client[1] has less knobs but still quite a few, and it doesn’t even do forwarding as far as I can see. [1] https://plan9.io/magic/man2html/1/ssh2 https://plan9.io/magic/man2html/1/ssh2
- lifeisstillgood 4y agoTo me the simplicity argument is the greater argument t for microservices We should stop seeing microservices as a technical problem / solution they are how to divide a "business domain" up into account the smallest constituent parts according to vat business view in the domain
- alehlopeh 4y agoThe part about constraints is kind of muddled. Constraining the scope of your project is not the same thing as working within a set of externally imposed constraints, which is what people are usually referring to in stories about how being forced to do more with less led to an unexpected innovation of some kind. The former is really just defining the scope of the project, which is covered in the following section.
- hinkley 4y agoI’ve had a few bosses give the speech about no heroes. If I’ve given a speech, well there are several but the one relevant here is instead of trying to build the perfect product, building the best product we can build. If you don’t follow that constraint you end up in Kernighan’s Law territory, and the wheels eventually come off. Know your strengths. Build up or compliment your weaknesses, stop trying to Fake It Til You Make It when you’ve made it most of the way to where you’re going to get.
- donatj 4y ago> Say No Getting junior devs to do this is like pulling teeth. Trying to get a feature stopped after they've built it is soul crushing for them. It's a problem. At this point I've all but given up beyond minimizing the blast radius in code review.
- cpach 4y agoThere ought to be a Zen lesson hidden here somewhere. Perhaps tech companies could have kickoffs/workshops where the participants would create sand mandalas together?
- __alexs 4y agoDepends. Can I put the mandala in my promo packet or not?
- jollybean 4y agoYes, this is more practical than anyone might imagine. The guy who just poured the concreted for a foundation, does he care that much that it's torn up or not use? Probably not, even though he's likely skilled and professional. We are far too precious.
- corrral 4y agoI know for a fact that a lot of people who build real things take pride in seeing them in use and still around many years later. I think they'd absolutely be demoralized if the typical case saw their work torn up and discarded without ever being used.
- roflyear 4y agoWhy are people going directly to your junior devs for feature requests?
- mobjack 4y agoBecause the senior dev always says no.
- light_hue_1 4y ago> Minimize Dependencies ... Consider doing it yourself. This is terrible advice! Maximize your dependencies. Adopt as much external code as possible. Build what you can with it. Then, as you reach the limits of those dependencies, and you absolutely understand what needs to get done replace them as you need to. The vast majority of what people write will be trashed and/or changed radically. You should adopt whatever tools are required to get things working minimally and then make decisions like this.
- FrancoisBosun 4y agoWhen the dependency is deprecated, I have to stop what I'm doing and replace the dependency. If the dependency has a show-stopper bug, I either have to wait, vendor the dependency, or rewrite. That's what the original article advocates for: be careful what you import. leftpad, probably write it yourself. React, OK to use, but maybe vendor.
- pjerem 4y agoYeah, dependencies makes you dependent :D
- swatcoder 4y agoIt sounds like you work in prototyping, which is cool, but a lot of us work in engineering and need more control and surety in the fit, quality, durability, and predictability than we can expect to find in the work of some stranger with no accountability to or insight into our project.
- oceanplexian 4y agoThis might start a fire here but I think dependencies are actually the problem, and see it happening in real time with all the latest gRPC offshoots for inter-service communication (Seems like there is a new one every day). The libraries attempt to "dumb down" TCP, HTTP, etc and treat them as an abstraction that you don't need to know the details of. But it ends up biting people in the a$$ because networking isn't a perfect world where every request succeeds, terminates cleanly, or goes to the destination you expect. Whisking away all the complexity makes developers dumber as they eschew solving low-level problems with over-engineered high-level solutions that paper over the underlying issue, e.g. using mTLS to get around the fact that you're using DHCP to assign address space to nodes incorrectly, or making every API request a POST because the designer didn't understand HTTP caching, and so on. You get these endless problems that were solved decades ago because people keep trying to reinvent the wheel.
- samsquire 4y agoIn my experience people refactor code to their own understanding of the problem and not all refactorings improve the code. People abstract before an abstraction is necessary. I find single file dense leetcode style code easier to understand and follow the flow. Algorithmic code I can reason around. A large mature codebase is far harder to get to know. One of the first things I do when I study a new codebase is find all the entry points and follow the flow of code from beginning to the thing I am interested in. One person's beauty is another person's mess. It's harder to change an existing codebase than to write a simple program that does the new thing but not in the context of the original program. A reference implementation of the various components is far easier to understand than one big ball of mud. Fitting problems together is hard. You need to understand the old thing before you can introduce the new thing and it ends up being forced or hacked in if the design doesn't support the new thing. I tend to write reference implementations of everything, then combine them together as a separate project. I find an empty file far more reassuring than a large codebase.
- awild 4y agoI try to encourage newcomers to refractor the code into a form they understand, fix the problem and then undo that refactoring as much as possible. If they actually come up with a better abstraction I'm up for it. Refactoring will give them the chance to see what the actually moving parts of code are.
- samsquire 4y agoI like your use of the words moving parts. Eventually code ends up looping over memory locations, copies, moves, adds, subtracts, multiplies, divides, reads, writes data or memory locations. All the files and code on the way to get this to happen such as Classes, parameters, arguments, variables, functions, methods, closures, objects are ideas of the languages compiler to abstract the instruction stream. Command line arguments, class constructors, URL query parameters, marshalling, JSON field names, method parameters, function arguments, HTTP headers, cookies, request objects, events are just complicated variations of passing data in the right shape. They are not the above list of "moving parts" or computation that is easy. In other words modern coding is just configuration. I feel the complexity of modern code is a problem we created. And I feel there's something missing. It's hard to update code. When I find the loop that does the thing, I feel I can understand the codebase such as the magic +1, -1 or the relationship of objects linked together in a data structure or the assignment to a list or array or variable. "How does that get to here"
- zwieback 4y agoIn my personal experience a lot of the mess stems from complex layer-to-layer interactions. Within my own modules I'm pretty good at keeping things clean but marshalling data from C# to C++ (or Python to C or this lib to that lib) is where I get sad. Or mapping return and error codes, or catching exceptions etc.... The cleanest code I write is for embedded systems without an OS, basically a sparkling gem of refactored goodness.
- snarf21 4y agoDrawing the line for separation concerns is one of the hardest things to do well in CS.
- a_c 4y agoNot only in CS, it is hard as organisation design as well. E.g. do you want an engineering team, or do you want a product team that has engineer so to eliminate silo. Should engineer care about hiring? Or that's HR's concern. What about security? What about how good the product is performing? What about customer feedback? Should engineer care for all that? Organisation of code is miniature version organisation of a company. Of you nail it, that's your secret sauce
- rr888 4y agoDB schema to DAOs to business interfaces to binary persistence and to json.
- conradfr 4y agoIf you try to apply the "write code that is easy to delete, not easy to extend" principle, your code will be less messy.
- b0afc375b5 4y agoReminds me of this (Greg Young - The art of destroying software): https://vimeo.com/108441214 https://vimeo.com/108441214 I'm still trying to figure out how to apply this to my personal javascript codebases.
- hu3 4y agoThis is also an argument in favor of functional programming over OOP class-based programming.
- stuckinhell 4y agoI like the ideas, but how many us have the political power at our jobs to say "No" on a project to a feature ?
- Hirrolot 4y agoFrom "A Philosophy of Software Design" [1]: > Ideally, when you have finished with each change, the system will have the structure it would have had if you had designed it from the start with that change in mind. [1] https://web.stanford.edu/~ouster/cgi-bin/book.php https://web.stanford.edu/~ouster/cgi-bin/book.php
- andrewallbright 4y agoAs a journeyman programmer, I have found a few tools to help me reduce complexity. Abstract interfacing techniques like base classes, abstract classes, and (my favorite) interfaces allow me to model interesting things. Thinking about relationships between things in my systems versus categorizing things helps me avoid the "if you want to do something in OOP you must first define the universe" type problems. DDD and conceptualizing how 'infrastructure' components interact with my main system is a nice guide for me. Trying to write good tests is how I'm able to bounce around a few projects without having to read source code to reload context. These are things that work for me. As I continue my practice I may find that I'm wrong or misinformed about some things. I should hope that I'll be able to incorporate a higher understanding as I gain more experience.
- camgunz 4y agoI think this is broadly an incentives and mindset problem. First, people generally don't hire me to set up a WordPress site (I should get into this though); they hire me to write something new and bespoke. So my skills are in exactly that: I build new stuff. Second, I'm pretty bored by the idea of gluing dependencies together. It's neat to see how fast or neatly I can do it, but that's good for a month or two tops. So if you want me to cook up new tech with a pretty good amount of code, I'm your guy. If you want me to carefully build something someone else has done 100x before while constantly having meetings about capitalization, line length, and coding-fad-of-the-week stuff, I can't handle it. My (totally rational, at least to me) response will be: customize a CMS for $10k, don't hire an engineering team for ~$500k. If I'm stuck on this project, I'll subconsciously try to introduce joy into my life by doing bad stuff, like writing a lot of cool new code where I shouldn't, and so on. Our incentives are misaligned. --- Or, you can think of it in terms of innovation tokens. Are you building a new database storage engine? Adopt the conventions of the database you're building it in; don't also try to innovate a new architecture/style. Are you building a new JS framework? All the innovation there is in developer experience, so all your innovation should go into abstractions and mental models; don't also include new, surprising algorithms. By picking a thing you are doing, be aware that there are 10000 other things you picking to not do.
- nolist_policy 4y agoAlso: If requirements change or new features are added, rather than only making the changes nescessary to existing code, rewrite code it touches. This forces you to keep all corner-cases in mind and will lead to more correct code. In a similar vein, when starting a new project form scratch, first do a quick and dirty prototype and then throw everything away and start anew. This way you know up-front what the challenges are.
- spc476 4y agoThat doesn't work, and it comes from experience. The messiest bit of our code base is the business logic, which out of necessity, is intertwined with I/O logic (we have very tight timing requirements, and we have to query several sources of data over the network) and we only ever add new requirements, never retire old ones (or at least, that's been the case over the past 12 years). Rewriting the code the new requirements touches is pretty much out of the question. About eight years ago I started a "proof-of-concept" that my manager asked for. I was using Lua for ease of development, and LPEG because it involved a ton of parsing. My intent was to get a handle on what was required and then do it C or C++. I found out a few months after the fact that my "proof-of-concept" was, in fact, in production and running. So much for my quick and dirty prototype. (And in retrospect, it hasn't turned out that bad---the code is way easier to deal with than our business logic in C/C++ because of Lua's coroutines make the event driven code look linear, and it's been fast enough).
- twawaaay 4y agoI think this is terrific advice. Over decades I have compiled my own list which contains all these and bunch of other behaviours that are needed for successful project. I would add one or two very important thing missing from the list. One, not explicitly mentioned but covered in other points is to plan for simplicity. Make simplicity an explicit goal of the project and set up process to remind of it at various important points in the process. For example, I have a checklist for adding a new technology which has a very long list of things you have to think about before adding new tech of any kind (like "is it possible to replicate it with couple pages of code"). My goal is to have tech stack so simple that newcomers can feel right at home and productive immediately. Even if I (we, me and my team, whatever) screw up, then the future owner will tend to have much easier time fixing it if we tried to keep it simple. My hardest challenges were not difficult technical problems (most backend applications tend to be very simple problem from technical point of view) but rather past teams that were very smart and created a monster so complex they themselves ground to a halt after some key people left. And connected to it (part of the checklist) is to be aware of when you are about add things to the project for intellectual gratification rather than practical purpose -- and cut it mercilessly out. Engineers tend to really dislike working same technology all over again, but this is what is needed to become really proficient. It seems exciting, but every time you add or change something in the stack you need to learn that thing (and accept being less productive for some time), you accept risk of new problems (and risk is a cost) and, finally, you cause the same to every team member and any future hire. And while it is easy to see the benefits of something, the costs and risks are usually much less understood before you have invested enough in it. And, additionally, frequently the benefits are much overvalued.
- ParetoOptimal 4y ago> Make simplicity an explicit goal of the project and set up process to remind of it at various important points in the process. Which altitude of simplicity is most valuable?
- deleted 4y ago[deleted]
- 4y ago
- wefarrell 4y agoPersonally I think the most important thing to minimizing code complexity is ensuring that it understandably maps to the business logic. The business logic is the essential complexity and everything else can be seen as waste. The first step is getting the lexicon right. Frequently the business lexicon is ambiguous in such a pervasive way that the people immersed in the business aren't aware of the discrepancies. For example I remember from working in healthcare the words "claim" and "member" often have very different meanings in different contexts and I would see developers hacking code together to get the data model of one context conform to the data model of another when they should have been treated as different entities.
- martinflack 4y agoThis is huge. Often the coder automating some manual process is the first person to sensibly create an unambiguous, correct taxonomy just to discuss it precisely.
- kristov 4y agoI agree. As a dev in a pretty large organization, I have seen the knowledge of business logic dissapate as the org grew, with some churn. To the point now where very few people actually know how the current system works, let alone how it is supposed to work. This means the only concrete definition of "this is what the system is supposed to do" is only in the code. The organization is disorganized, and the code is only as good as the level of organization outside the code - so very poor. All this gotme thinking about what does it mean exactly to be organized? We call companies "organizations" because they are groups of people getting together and organizing. If the organization is not organized the code (or any other artifact it produces) will also be poorly organized. Organizing is sorting, classifying, grouping, communicating etc.
- wefarrell 4y agoOh god you just brought back a memory from when I was brought in to manage a team at a dysfunctional organization and I was trying to figure out how a complex service was supposed to work. I asked: "Do you have any documentation or requirements", I was told "The code is the requirements", to which I responded "Wonderful that means there can't ever be bugs because there will never be a discrepancy between the code and requirements". Getting requirements in writing was an uphill battle and the lack of requirements always wound up screwing over the developers because there was no contract to prevent scope creep and the developers were the ones that were held accountable for misunderstood features and missed deadlines. As a result everything was constantly rushed and not well thought out. It took me a long time to convince my boss that the issue stemmed from unwritten requirements and a lack of planning.
- viktorcode 4y ago> If you’ve been developing software for a while, you know that code has this natural tendency to turn into a mess. That doesn't apply to everyone and every project. When people leading the project are experienced developers understanding clean architecture the mess is just an oversight which is local and can easily be corrected.
- larsonnn 4y agoSet up your room like you would write your code and look if you are happy with that.
- deleted 4y ago[deleted]
- A7med 4y agoEasier said than done, when working with a big team and the code has been edited multiple times over a long period of time, no one will risk touching that code
- HarHarVeryFunny 4y agoIn the real world you can't (and shouldn't) always say no to evolving and added functionality, nor can you always make unanticipated changes in the cleanest way given project deadlines. The real solution is to recognize when new features are slower and messier to implement than they should be (because the evolving requirements have outgrown your original design), and periodically take the time to refactor to clean things up.
- spaetzleesser 4y agoIt almost boils down to "have good taste and understand the problem well". One thing I have noticed is that it's almost impossible to keep a codebase under control over time when business needs change and new people come in all the time.
- cle 4y agoIMO code should be messy to some degree. If it's not, then I'm moving too slowly. Probably over-refactoring and over-analyzing. And usually that's a symptom of something deeper, like too much ambiguity leading to procrastination, which I can fix by scoping in more detail, writing throwaway code (ex a proof-of-concept), etc. Obviously we don't want a complete dumpster fire of a codebase, but some mess is inevitable and healthy. First see the mess, then refactor. Refactoring before the mess is how you end up with crappy abstractions. In the past few years I've adopted the attitude that code cleanliness isn't really that big of a deal. There are some obvious guidelines to follow around readability, encapsulation, etc., but these days I care more about system architecture than I do the code itself. Localized code is easy to change/refactor/clean up, the system itself is not.
- OnlyMortal 4y agoA “mess” is subjective. For sure, code that can’t be maintained by the developers you have is a major issue. That’s your first problem to resolve. Changing code that works, even if it’s a rock you ought to put down, is a risk.
- flappyeagle 4y agoTelling people to define clear goals doesn't help them define clear goals, unfortunately. It usually requires a lot of coaching before developers are self-aware enough to even understand what a clear goal looks like.
- deleted 4y ago[deleted]
- ilrwbwrkhv 4y agoGood code is about responsibility. The less things know about other things the better it is. Unfortunately this only comes with experience and concentrated improvement. After a while it is indeed an art form.
- jiggawatts 4y agoSomething that shocked me was working with junior programmers for the first time. For decades, I had either worked solo or with other experienced developers. It was an eye-opening experience. My style is influenced by Haskell and Rust, even when I program in, say, C# or PowerShell. A simple example: I will extract the read-only logic into a pure function and minimise the size of the mutable procedure. This makes it trivial to test the logic in isolation without triggering any side effects. Similarly, the logic can have convoluted control flow but the imperative code can then wrap that with a single try-catch block, transaction, or retry loop. For me this was such second nature that I didn’t even realise I was doing it until I saw the imperative spaghetti written by the juniors. I tried to explain with pair programming sessions what the benefits are of my approach. Without fail, they would just “hack something” into the existing spaghetti, adding yet another mutable global variable to track some new state. In every case they said they were in a hurry and that they would “fix it later”. I replied: “there is no later.”
- agent281 4y agoThis is my current struggle. I've enjoyed mentoring in the past, but right now I'm getting very exasperated feedback. It's hard because I don't have control of the environment to alleviate the deadline pressure, but I still want to help people learn. The end result seems to be a pile of tech debt for now. C'est la vie.
- jiggawatts 4y agoSomething I observed very early on in my career is that bugs will have to be fixed no matter what. You can put them on the todo list and fix them later, or fix them right now. Either way, you're going to have to do the task. It's like a conservation rule in physics, for every bug found, a bug fix must eventually be implemented. Bug in, fix out. But... if you leave a bug lingering, then it can cause test failures for unrelated code development. It can trip up other developers. It can cause false positives until resolved. So the only logical conclusion is that all bugs must be fixed ASAP, otherwise they have a "multiplier" factor dependent on how long they're allowed to persist. If left unchecked, this can blow out exponentially, until you're unable to efficiently fix bugs because you're tripping over thousands of other unfixed bugs while doing so. You would think this kind of thing is logical, but no-one ever believes me. There's just slow blinking and then a slower repeat of the same old mantra: "We'll fix it... later?"