[HN Gopher] Code doesn't have to be a mess
___________________________________________________________________
Code doesn't have to be a mess
Author : dsieger
Score : 151 points
Date : 2022-07-25 14:08 UTC (8 hours ago)
(HTM) web link (www.danielsieger.com)
(TXT) w3m dump (www.danielsieger.com)
| light_hue_1 wrote:
| > 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 wrote:
| When 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 wrote:
| Yeah, dependencies makes you dependent :D
| scrapheap wrote:
| Honestly, I think you're both wrong. You shouldn't be trying to
| minimize or maximize your use of dependencies. You should add a
| dependency when it makes sense and write your own code when it
| makes sense.
|
| Doing more complex time and date work, then a good solid
| library for manipulating datetime variables will save your
| sanity. Need to right justify a string to set length then using
| a leftpad will leave you at the mercy of a random author on
| npm.
| wheybags wrote:
| To me, the best case scenario is adding a dependency of
| medium size and complexity that you're confident you could
| write yourself. This means that if you run into problems, you
| can just shrug, and then ditch the library, but if it's ok
| then you save some time. What's terrifying is dependencies so
| large that you can't fathom the amount of effort required to
| make them. It also makes it much harder to tell if the
| library is actually any good. Luckily for your example of
| time libraries there's normally a "blessed" library for
| whatever ecosystem you're in. Tiny / super simple
| dependencies are a complete waste of time, if I can write it
| in < 1hr I would much prefer to do so.
|
| IMO there's a lot to be said for writing your own version
| that does 60% of what some library does, but 100% of what you
| need it to do.
| swatcoder wrote:
| It 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 wrote:
| This 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.
| intelVISA wrote:
| Agreed, it's not like you'll really forget how to write a
| Berkeley socket; couple of deques, mutexes and an
| <arpa/inet.h> header later just saved you forty gigs of BOOST
| BEAST.
| A7med wrote:
| Easier 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
| flappyeagle wrote:
| Telling 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.
| stuckinhell wrote:
| I like the ideas, but how many us have the political power at our
| jobs to say "No" on a project to a feature ?
| OnlyMortal wrote:
| A "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.
| zwieback wrote:
| In 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.
| rr888 wrote:
| DB schema to DAOs to business interfaces to binary persistence
| and to json.
| snarf21 wrote:
| Drawing the line for separation concerns is one of the hardest
| things to do well in CS.
| a_c wrote:
| Not 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
| andrewallbright wrote:
| As 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.
| alehlopeh wrote:
| The 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 wrote:
| I'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.
| HarHarVeryFunny wrote:
| In 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.
| cle wrote:
| IMO 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.
| nolist_policy wrote:
| Also: 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 wrote:
| That 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).
| spaetzleesser wrote:
| It 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.
| camgunz wrote:
| I 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.
| Hirrolot wrote:
| From "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
| [deleted]
| twawaaay wrote:
| I 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.
| dsieger wrote:
| Thanks for your comments, this is helpful.
|
| I especially like your point about planning for simplicity and
| making it an explicit design coal. Communicating this clearly
| to the team seems super important.
|
| As for adding things out of intellectual gratification: This is
| so true. I've seen this all too often, myself included. Many
| good engineers are curious by nature, and it can be tough to
| restrict this curiosity. Maybe it's a question of having
| different outlets for that sort of creativity, either at work
| or in private.
|
| If your list is available in some form, I'd be curious to have
| a look!
| twawaaay wrote:
| The list is long but if I wanted anybody to take one thing
| from it is that complexity is the real killer.
|
| The job of the most experienced person in the software
| development organisation should be to spot unnecessary
| complexities and find ways to eliminate them.
|
| The issue is, most experienced people tend to be engaged in
| activities for political reasons like adding new technology
| -- which tends to be perceived as more valuable than removing
| it.
|
| I might be biased (by selection). I am called upon to join
| and help projects that face significant problems (emergencies
| all the time, no time to breathe, way behind schedules,
| unable to deliver anything, etc.) But every time I join a
| project like that, the repairing process tends to start with
| removing stuff rather than adding. And if stuff needs to be
| added this is usually so that it makes possible to remove
| much, much more of complexity somewhere.
|
| As an example, we have stabilised at least 3 projects by
| removing "microservices" and rolling everything to a single
| monolithic application. Not saying microservices is a wrong
| idea, but saying it might be wrong for a particular project
| without strong automation culture, tooling and without large
| enough problem to solve. Somehow this always starts with
| strong opposition and ends with happy people that can code
| and not spend significant portion of their time dealing with
| complex, flaky infrastructure.
|
| My rule as I present it to the team is "I want at most one of
| anything unless we understand exactly why we need more than
| one." So one programming language (unless you need one for
| backend and one for frontend, then we need two), one
| application (unless you have multiple teams and then one per
| team might be better to make them independent), one
| repository, one cloud infrastructure provider (AWS tries to
| be on parity with GCP, why do you need something from GCP
| just for it being incrementally better?), one place to store
| all documentation and procedures, one database tech (do you
| really need half of the application use MongoDB and another
| half use Postgres?), etc.
|
| The rule might sound childish, but it is simple and helps
| people make better decisions on their own which is
| essentially what you as a tech lead want.
| traeregan wrote:
| Any chance that your list is organized in a way that you
| wouldn't mind sharing it? I've been on a documenting spree the
| past few years at work (after ~14 years of not documenting
| much), and it would be great to see your decades-long process
| notes. I totally understand if that's your secret sauce and
| you'd prefer to keep it that way though.
| donatj wrote:
| > 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.
| roflyear wrote:
| Why are people going directly to your junior devs for feature
| requests?
| mobjack wrote:
| Because the senior dev always says no.
| korijn wrote:
| Being married to your code/output is just another flaw typical
| for juniors. Put them on features that aren't critical or
| ensure they are made aware up front that their work may be
| rejected if it doesn't meet design expectations.
| camgunz wrote:
| I'm not a junior dev and I still get peeved when my time is
| wasted. I guess I would only not care if I didn't care about
| what I was working on, but that's a different kind of
| existential torment haha.
|
| Beyond opportunity cost, you can think of it as deleterious
| to your performance. If 10% of your work never gets merged
| because of shifting priorities, compared with someone else
| who has miraculously dodged these problems, that's pretty
| unfair.
| corrral wrote:
| 10%? Holy shit, I'd say a solid 90% of my output never
| brought enough value to justify doing it in the first
| place. Much of it without ever seeing a real user. Software
| development feels like digging holes and filling them back
| up again, over and over, to me.
| camgunz wrote:
| Feels like a recipe for burnout IMO; that would be tough
| for me to deal with.
| Cerium wrote:
| Try to direct your personal energy away from getting your
| code into the product and towards making the best product
| possible. Often after building a feature it is obvious that
| it makes the product worse. I've had many such features
| cut.
| camgunz wrote:
| My irritation comes from that decision/realization
| happening after we already devoted engineering time to
| something--the most expensive time to reverse yourself.
| lowercased wrote:
| I've hit this many times over the last 20+ years, and
| have come to recognize/accept that _most people_ can not
| really grok something until they can 'use' it in some
| capacity, often with 'real' data.
|
| Clickable wireframes, design sessions, mockups, etc -
| they can all help explore ideas _before_ code, and
| potentially save things. I 've had numerous examples
| where I can identify "this is confusing" or "this doesn't
| solve the problem, just moves it around a bit" and I'm
| usually 'outvoted' by others, and do the work. It's
| usually only after it's in peoples' hands that they
| identify the rough edges (or more).
| camgunz wrote:
| 100% agree; I'm a huge fan of the design and product
| process. In particularly I feel like every product team
| needs a designer--probably not dedicated, but like,
| dedicated designer hours. It's yet another case of "1
| hour (of design) upfront saves 20 engineer hours further
| on". I've had the (mis?)fortune of working on teams with
| and without designers and the difference is super
| obvious, at least to me anyway.
| klibertp wrote:
| > that's pretty unfair.
|
| Hilarious. I wonder what a plumber or carpenter would say
| if you were to complain to them on how unfair your job is,
| because 10% of your output doesn't show in the finished
| product, yet _you are still paid_ for that output.
| Imagining the reaction to that just made my day.
| pflenker wrote:
| Plumbers and carpenters find purpose in what they do, and
| so do engineers. Getting paid is just one factor of
| several for job satisfaction.
| wutbrodo wrote:
| The enormous silliness of this argument doesn't seem to
| stop it from popping up all over the place. People's
| expectations are relative to their environment. The large
| majority of things you might think to complain about in
| your life would look hilarious to a caveman or a medieval
| peasant or even someone from 1980. Similarly, the vast
| majority of HN users are extremely high-percentile for
| global wealth and income: this would be a very boring
| place if people bought in to your paralyzing insistence
| that you can't ever discuss improvements to something
| because problems larger than it exist somewhere in the
| world.
|
| It's a worldview that's so nonsensical that it's its own
| reductio ad absurdum: If a fast-food worker complained
| about wanting to be treated with dignity at work, would
| you similarly scoff at them because coal miners or
| sweatshop workers don't even get physical safety?
|
| Having high standards is a _good_ thing. It's the
| hallmark of society's progress. It doesn't preclude being
| grateful for the privileges you do have, and it's nothing
| to be ashamed of unless your self-esteem is so low that
| you think you don't deserve to be treated well.
| klibertp wrote:
| These two things taken together:
|
| > People's expectations are relative to their
| environment. [...] Similarly, the vast majority of HN
| users are extremely high-percentile for global wealth and
| income
|
| > Having high standards is a _good_ thing. It's the
| hallmark of society's progress.
|
| seem to suggest you subscribe to the "trickle-down"
| ideology. I don't. No, having pockets of "extremely high-
| percentile" people who are entitled to complain about
| "unfairness of 10% of their work not being appreciated"
| is not a hallmark of _society_ progress. It 's closer to
| systemic exploitation. It's a pattern we should know very
| well from history lessons. No bread? Let them eat cake!
| Sure. Just brace for the impact when the bubble bursts -
| there's a sharp blade at the end of this road.
|
| > it's nothing to be ashamed of unless your self-esteem
| is so low that you think you don't deserve to be treated
| well.
|
| Because having 100% of someone's work accepted as useful
| when it's not - for whatever reason - is a basic human
| right that everyone deserves. That's called "being
| treated well". I didn't know; I thought not getting 100%
| sunny days in a year is called "just life", but now I
| know it's a violation of my rights. How could I be so
| wrong for so long?
|
| I'm being sarcastic, but you have to accept this comment
| in its entirety and tell me how happy you are that I
| wrote it for you. I put work into writing it. I deserve
| being praised for it, no matter how much you like what I
| wrote. Right? Please, do treat me well.
|
| How's that for a reductio ad absurdum?
| camgunz wrote:
| > Because having 100% of someone's work accepted as
| useful when it's not - for whatever reason - is a basic
| human right that everyone deserves.
|
| Again this wasn't what I was saying. My argument is that
| engineering time (and time in general) is valuable, and
| we should be careful how we spend it. In other words: if,
| over the course of a project, you're wasting a lot of
| time, that's bad if you care about the project (and you
| probably should). Maybe you disagree, but I don't think
| this falls under the "entitled millennial SWE" category,
| but rather the "we can do better" category.
|
| I'll also, for the sake of discussion here, say I've done
| some pretty shitty work and have benefited tremendously
| from code review and general discussions with my
| colleagues. I'm definitely not someone who thinks they're
| a "extremely high-percentile" person, probably above
| average, but definitely not like a Brad Fitzgerald or
| something.
| camgunz wrote:
| > I wonder what a plumber or carpenter would say if you
| were to complain to them on how unfair your job is
|
| I feel like this is a pretty uncharitable caricature of
| my position. I'm not whining about all my work not
| getting in. I'm saying, "We're building houses for poor
| people, I care about this, you had me spend a week on
| this thing you said was going into one of the houses, you
| were wrong, I blew a week of work that could've gone to
| building the houses, and winter is coming". It's not
| about me personally, it's about me caring about
| efficiency.
|
| > yet _you are still paid_ for that output
|
| I don't only work to be paid. It's a necessary but not
| sufficient component. I try to find fulfilling work that
| I think improves peoples' lives, and I'm fortunate enough
| to achieve that more often than not. I'm not saying I'm
| not selfish, just that I'm not _entirely_ selfish, haha.
| TranquilMarmot wrote:
| Time you got paid for is not time wasted ;)
| camgunz wrote:
| Hah, an older colleague taught me "it pays the same" as a
| kind of mantra for dealing with the capriciousness of
| management. Feels cynical, but I've gotten a lot of use
| out of it over the years.
| dagss wrote:
| If only 10% of your work is thrown away consider yourself
| very lucky, I would think of that would be a minimum number
| in the industry...
|
| Myself I not too long ago did a 6 month crunch with the
| rest of my team on a product that was cancelled right
| before launch...
| Hirrolot wrote:
| > Trying to get a feature stopped after they've built it is
| soul crushing for them.
|
| That's why I suggest first filing an issue, discuss a design,
| and only then actually implement the feature. Will save you
| many hours.
| gxs wrote:
| This sounds odd to me.
|
| Is there no planning flow where you work/have worked? I
| understand giving developers freedom to build, but some sort of
| oversight by someone with a view of everything that's going on
| is also necessary.
|
| Typically there are entire planning exercises that happen
| before stuff even hits a JIRA board.
|
| These are usually conducted by Product Managers, Project
| Managers, analysts, and often in startups the CEO themselves.
|
| The fact that people are off building features willy nilly
| sounds like it would contribute to messy code base.
|
| Knowing what's next helps plan the work on the developer side
| as well which also minimizes the blast radius.
| angarg12 wrote:
| I believe many companies also incentivize this. Not many people
| get praise, raises and promos by saying no or simplifying
| things, much less juniors. Once you've built something and put
| it in your promo doc, no one wants to remove it or say it was
| the wrong thing to build.
|
| As an example, I did an spike to explore a request from another
| team. I wrote a short document with my findings and recommended
| against it due to the cost/benefit analysis. My manager told me
| not to use this story in my performance review since "we don't
| want to display failures".
| cpach wrote:
| There 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 wrote:
| Depends. Can I put the mandala in my promo packet or not?
| jollybean wrote:
| Yes, 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 wrote:
| I 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.
| conradfr wrote:
| If you try to apply the "write code that is easy to delete, not
| easy to extend" principle, your code will be less messy.
| hu3 wrote:
| This is also an argument in favor of functional programming
| over OOP class-based programming.
| b0afc375b5 wrote:
| Reminds me of this (Greg Young - The art of destroying
| software):
|
| https://vimeo.com/108441214
|
| I'm still trying to figure out how to apply this to my personal
| javascript codebases.
| viktorcode wrote:
| > 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.
| lifeisstillgood wrote:
| To 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
| rbongers wrote:
| There'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.
| pca006132 wrote:
| Just curious, have you ever seen a project that is structured
| nicely according to this criteria that you can share?
| commonlisper wrote:
| I 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.
| ics wrote:
| Having not read the book OP mentioned, I would say it's
| important to emphasize that _structure_ does not necessarily
| mean _abstraction_. Starting a new project is more like
| getting groceries and stocking the pantry than it is starting
| a stew and agonizing over which type of onions to toss in.
|
| Good abstractions evolve naturally out of good structure.
| jewel wrote:
| Sometimes 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.
| [deleted]
| samsquire wrote:
| In 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.
| intelVISA wrote:
| Generally most of my refactoring is cutting out code; I love an
| excuse to chunk down excessively verbose classes.. maybe I'm
| weird but I generally prefer a lean, concise codebase that
| might share a few free functions than a perfect OOP pyramid.
| galdosdi wrote:
| What a great way to do it, too, because once you're done, your
| "reference implementations" can later serve as test harnesses
| if need be.
|
| > I tend to write reference implementations of everything, then
| combine them together as a separate project.
| awild wrote:
| I 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 wrote:
| I 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"
| pmarreck wrote:
| No mention here is made of test coverage (hopefully because
| everyone already knows); to refactor without near-100% test
| coverage is insanity.
| spaetzleesser wrote:
| "In my experience people refactor code to their own
| understanding of the problem and not all refactorings improve
| the code."
|
| Same for rewrites. Often the rewrite will have the same number
| of problems, just different ones.
| alphanumeric0 wrote:
| > 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.
|
| I genuinely can't tell if you're being serious or not. If you
| are, do you also like to read books written as one giant
| chapter? Or entire chapters as one giant paragraph?
| fleddr wrote:
| The book analogy you use isn't very accurate. Even if you
| merge chapters and paragraphs like that, you still read it
| sequentially. Just in a less comfortable way.
|
| Which is not at all like a modern codebase that is modular,
| abstracted, etc. If you're new to a codebase, and want to
| understand one particular feature, you'd likely need to jump
| back and forth across 10 files.
|
| It's not far-fetched to say that makes it difficult to
| understand.
| tharkun__ wrote:
| The thing is that if you just need to understand a specific
| part of something you will need to jump as well even if
| everything you needed for that one thing is written
| sequentially in one file. You will want to skip over
| implentation details of certain things to get the general
| picture first on a more abstract level.
|
| Let's say you have a simple endpoint that takes a list of
| comma separated inputs, parses them as numbers and spits
| back a sorted version of that.
|
| I don't want to see a version of that, which a compiler
| might have inlined. Including the implementation of the
| sorting algorithm. I only want to see a high level of
| abstraction version of it. Basically just something like
| (pseudo code in a non existent language) :
| fun endpoint(input): inputs[] = split(input, ',')
| numbers[] = parseAsIntegers(inputs) return
| quicksort(numbers)
|
| I can easily understand what this does and what the idea is
| behind this "algorithm" in 3 lines. If I had the "inlined"
| version of this I would have to manually identify each of
| these parts and potentially skip over tens to hundreds of
| lines.
|
| I think this is really a bit about trust. Do you trust that
| these named functions I am calling do what their name does?
| Does quicksort actually do a quicksort or has someone
| implemented bubble sort in there? Of course this is a
| minimal example and especially quicksort would probavly
| just be a library but imagine all of these were large
| complex pieces of our code base.
|
| Personally I am an advocate for using functions (methods or
| whatever your language calls them etc) and naming them
| properly and then trusting those names by default. I want
| to spend time making this nice and understandable and
| abstracted once when writing. Not every time someone reads
| it. As soon as something does not seem to behave in the way
| the name suggests I will then and only then go check the
| actual implementation and for example find out that
| parseAsIntegers actually also supports floats and quicksort
| is not actually quicksort but bubblesort and that is why
| this endpoint was slow etc.
| fleddr wrote:
| You can easily understand what it does because you picked
| an example that is easy to understand :)
|
| This discussion often ends up in the extremes: inline
| everything versus abstract everything. I don't think
| anybody reasonable would opt for either of those
| extremes, we should focus on the very large middle ground
| where there's a lot of subjectivity.
|
| Trust me on this, I've been raised on the DRY dogma and
| all related architectural patterns in favor of
| abstraction. I've lived the life, for 2 decades. But I
| cannot ignore the outcomes. Most codebases are extremely
| difficult to understand and it's very painful to change
| things. As 90% of all software development is
| maintenance, that's a planetary-sized problem.
|
| This doesn't mean you should inline everything, it means
| sane choices. As a simple example, say you're using a
| literal in your code:
|
| (if orderAmount > 100000)
|
| This code is incredibly easy to read. Common convention
| says to put this in a constant, at the top of the file.
| Old me agrees, new me does not. For as long as it's the
| only occurrence of the value, it doesn't need
| abstraction. The only thing it would do is make the code
| more difficult to understand. The very eager abstracter
| might even put that constant in a separate file.
|
| The point of this example is to abstract based on real
| reuse, not imagined reuse. I'm not against abstraction
| only against unnecessary abstraction.
|
| A second example. Say you have a reusable UI component. A
| change request comes in that's pretty large and specific
| for one niche need. It kind of goes against the spirit of
| the initial purpose of the component but is still related
| enough to consider it in scope of the component.
|
| Old me might add a "toggle" to the component, after which
| it can render in two modes. Sometimes called a "god
| component". This approach sucks. It makes the component
| much more complicated and changing and testing it becomes
| a nightmare.
|
| Even older me would break down the component into smaller
| components and then "compose" them based on its mode.
| This is even worse, now you have to jump around many
| places whilst the sub components are never actually
| reused (pointless abstraction).
|
| New me says fuck it and splits the component in two.
| Allowing significant code duplication between both
| components. It's not as radical as it sounds, it's in
| fact incredibly comforting. Each component can easily be
| understood (less complexity) and making changes becomes
| far less stressful as your blast radius is tiny.
|
| Developers spent the vast majority of their time not
| coding, instead figuring out how something works and how
| to make a change that doesn't break anything.
| samsquire wrote:
| At one point in time I used OpenGrok to try understand large
| projects.
|
| Without documentation I find large projects difficult to
| understand. There's literally too many global symbols and I
| cannot see the forest for the trees.
|
| What's the model of this program? What are the core
| principles that the author is using? Do I really need to read
| every file to understand what is going on?
|
| With Leetcode style programs there's one file with everything
| in it and I can usually find the entry point. The problem is
| well defined.
|
| I can see the moving parts in a Leetcode style problem. The
| looping, the data structure creation and control flow, arrays
| and recursion.
|
| Large mature codebases such as Java projects have thousands
| to millions of small files and packages it can be difficult
| to see how things fit together, every file seems to be 10-20
| lines long.
|
| I like C projects as they have lots of code in one file.
| Everything I need to understand a module is in one file and I
| can use vim folding.
| zem wrote:
| back when i inherited a legacy C codebase i found
| https://www.jgrasp.org/ pretty useful to explore large
| files.
| josephg wrote:
| I've been programming for nearly 30 years and I feel the
| same way. 1000loc+ files are increasingly common in my code
| bases. I find it really hard to read code with lots of tiny
| files that individually don't do anything. It's like the
| programmer is embarrassed by their code so they're making
| me search for the core logic.
|
| Splitting code into multiple files really only makes sense
| to me when there's a clear division of responsibilities.
| That can mean a lot of things - like client / server,
| utility methods / core algorithm or class A / class B. But
| plenty of complex data structures are much easier to read
| and understand all at once. For example, I have a rust rope
| library which implements a skip list of gap buffers. The
| skip list is one (big) file. The gap buffer is another
| file. Easy.
| sanitycheck wrote:
| Same. Well, 1000 is an outlier but 300-600 feels right. I
| sometimes feel bad for doing it, because it's not what
| some other people might consider good code to look like.
| I occasionally have to do code review or help fix a bug
| in a the other kind of codebase and even the devs often
| don't seem to understand how those thousand 15-line files
| all fit together.
| alphanumeric0 wrote:
| I could see this working with vim folding. That's an
| interesting approach. I never really used folding in IDEs.
| kuroguro wrote:
| I think he is, can second that. Not having to jump around 10
| files with multiple classes and remembering where goes what
| usually means I can understand the code faster.
|
| Not sure about literature but for almost any technical matter
| I prefer learning from the smaller details instead of the big
| picture - it's often too vague and just doesn't stick in my
| memory.
| kreeben wrote:
| I have trouble with you equating "leetcode style" with "one
| giant chapter" and also with you equating "enterprise code"
| with one chapter following another, because when I read
| enterprise code it's
|
| 1. read one line of first chapter,
|
| 2. then skip to the last sentence of the middle chapter,
|
| 3. then realize the first chapter was actually the
| penultimate,
|
| 4. then read the forth sentence of the first paragraph of the
| second chapter, bearing in mind what you have learned,
|
| 5. then throw your hands up in the air in dispair
| alphanumeric0 wrote:
| Jumping around is the nature of code in general, whether
| its in a 1000-line file or split up amongst multiple files.
|
| For a maintenance programmer, they may already understand
| how everything works. They aren't following a particular
| code path, necessarily. Maybe they're working on a new
| feature and they need to re-familiarize themselves with
| previous chapters. It that case, it's nice to jump to a
| file that concerns itself with things grouped together.
| kreeben wrote:
| >> things grouped nicely together
|
| fixed it for ya :)
|
| I'm a maintenance programmer. Even working layers of
| layers of layers above the actual shit does not make it
| not stink.
|
| >> jumping around
|
| ...should be intuitive and joyful, not a disaster to your
| brain.
|
| EDIT: I am a fan, though, of SOC. I guess enterprisey
| code tries to be that (but fails hard at it).
| alphanumeric0 wrote:
| Okay, great. You are a maintenance programmer. You have a
| 1000-line program all in one file. You need to update the
| e-mail functionality of this program. You aren't
| following a stack trace or following a particular code
| path. There are 15 or 16 different e-mail related
| functions. How do you find the e-mail function you need
| to update? Do you memorize line numbers? Use regex
| search? Do you have vim marks setup?
| he0001 wrote:
| > One person's beauty is another person's mess.
|
| This is so true. Also I believe that when the original author
| wrote the code he had a (hopefully) clear vision of the
| solution. He wrote it as tidy and fitting to the problem as he
| saw it. Then sometime later someone else comes in and is
| supposed to alter the code in a way which does not fit the
| original author's idea of the problem. This creates a mismatch.
| The new guy can't and won't change the code too much, as it is
| too risky/much to do and therefore will only do as little as
| possible to make his change. Then some other comes along and do
| some more changes, which again isn't enough etc etc, et voila,
| you have a ball of mud that screams of a rewrite.
| hinkley wrote:
| Maybe I'm weird but a lot of my refactoring actually
| concretizes overly abstract code. It's easier to think about
| adding functionality to a block of code when you acknowledge
| that at the moment it only does 2 things, rather than using
| obscure wishy-washy language that implies it could do a dozen
| things.
|
| Where I'm definitely weird is that I have a higher verbal score
| than your typical developer, _and_ I 'm not afraid to use a
| thesaurus to find a better word for something. Too often we end
| up recycling jargon in situations where they are not quite
| doing the same thing but nobody could be arsed to open
| thesaurus.com and find a word that telegraphs, "B is like A but
| is not actually A."
| the_other wrote:
| I had to look up "telegraph" to confirm I'd understood you
| correctly. I don't recall seeing it used in the context of
| describing language, so I doubted my interpretation. I've
| heard it used most in discussions of boxing: a boxer's
| posture or their sequence of muscle activations _telegraph_
| their planned attack such that their opponent has time to
| block or counter.
|
| I like the way you used it. I'll try to use that myself.
| layer8 wrote:
| It means communicating something quite clearly but by
| indirect means (and possibly inadvertently).
| hinkley wrote:
| I've had this argument in code reviews a couple of times:
|
| Them (on the subject of 4 new methods): hey why did you
| make method 3 look "weird"? You should make it look like
| the other three.
|
| Me: because one of these methods can set the building on
| fire, and the rest can't. I bet you can guess which one
| is the dangerous one. Works as expected.
| nicoburns wrote:
| > It's easier to think about adding functionality to a block
| of code when you acknowledge that at the moment it only does
| 2 things, rather than using obscure wishy-washy language that
| implies it could do a dozen things.
|
| This is why Sum Types are so great. They give you an option
| between "concrete type" and "any type that implements this
| interface": "one of these specially enumerated types".
| efa wrote:
| >>People abstract before an abstraction is necessary.
|
| This!
| joe_the_user wrote:
| One of my recent coding experiences was teach a friend in grad
| school(MA) to code sufficiently well to finish his Master's
| project.
|
| Refactoring was absolutely necessary for this. He was writing a
| single simulation program that was single file in size. But
| once he'd created ten subroutines all changing a raft of the
| global variables, the slightest changes produced hair-raising
| bugs that he'd obsessively dive into debugging.
|
| The intuitions of structured programming and object oriented
| are more important than absolute fidelity. My points were: "If
| you can't have an object here, at least have a well defined,
| standard interface to values that need to be in a consistent
| state" and "decompose long action sequences into subroutines
| and if you can't do that, least group similar actions with
| similar actions in that long action sequence".
|
| Which is to say a given piece of code might not the structure
| you want but if has a structure, that can be enough. But then
| again, that piece of code might not structure at all and then
| rewriting it really is necessary and often is easier than
| debugging it a few time.
|
| And working with a large piece of "bad" corporate code, I've
| more than once that you something with one sensible if
| idiosyncratic structure that was refactored more than once by
| people who didn't understand the structure and imposed their
| own structure on just part of the code. But through an exercise
| in archeology, one can make the whole artifact work.
|
| But that doesn't mean you can't have code that is a true mess
| when the writer has no experience and no concern with
| structure.
| OJFord wrote:
| Is it deliberate that your comment itself is like an instance
| of the methodology you describe? Small separate stand-alone
| thoughts vs. a well-understood description (harder to write, to
| consume) in longer form commentary?
|
| That's not a criticism at all by the way, I just found it
| striking.
| samsquire wrote:
| It was accident and not deliberate but you may reveal how I
| think. Thank you for this insight.
|
| In hindsight if you focus on three questions: is it good? Is
| it right? Is it true? You'll head toward a good direction
|
| Synthesis of ideas is really important and the building
| blocks of understanding are fascinating. Programming and
| mathematics is taught as building blocks and then deliberate
| practice.
|
| I really enjoy reading plain descriptions of things,
| especially of other people's code.
|
| If you understand the core insight, difficult things can be
| easier to understand and apply for you.
|
| I really want to understand how tracing compilers work and
| LuaJIT, JVM and V8 but I found the code a bit too hard to
| understand as I jumped into the wrong locations.
|
| There has been two instances where Wikipedia was enough for
| me to understand and write an algorithm that implemented the
| description. Wikipedia doesn't have pseudocode for
| multiversion concurrency control but it does have an accurate
| if subtle description. I did the same for btrees but I did
| read some other people's implementations to get a feel. I of
| course wrote mine completely differently.
|
| I want people to document their code enough so that the core
| principles or idea behind their code could be reimplemented
| by someone else just by reading the description of how it
| works.
|
| Rpython and Pypy documentation is good but I still don't
| understand it enough to implement what it does. Which means
| I'm missing some detail or core insight.
| pyrolistical wrote:
| > I tend to write reference implementations of everything, then
| combine them together as a separate project.
|
| This is how I write software. My stackblitz is full of domain
| independent experiments https://stackblitz.com/@Pyrolistical. I
| then copypasta this into private projects once I figured out
| how the individual piece works.
| josephg wrote:
| Yeah I love this approach too. I've been learning CRDTs
| lately and I've gotten so much value from making tiny,
| inefficient reference implementations of things before diving
| in and optimizing. Toy implementations shake out all your
| misunderstandings of your design, and you can refactor like
| crazy. Going from simple code that works to complex code that
| works is much easier than creating the complex code correctly
| from scratch, in situ.
| pyrolistical wrote:
| Also I've discovered (and reported) so many bugs when I
| realized my very simple toy example was broken
| xedrac wrote:
| > People abstract before an abstraction is necessary.
|
| This one really frustrates me. Write code to the complexity
| level needed to solve the problem, and nothing more. The only
| time I'd break from this is if I know for certain that the
| added complexity is going to be necessary in the near term.
|
| > ... not all refactorings improve the code.
|
| While true, I have a low tolerance for code that requires
| constant bug fixing, or is so _overly_ complex that the thought
| of modifying it makes you want to cry. Some projects truly
| require that level of complexity. But in my experience many do
| not, and once you 've gained a solid understanding of the
| problems it is trying to solve, incremental refactoring is a
| fantastic way to improve the code's stability and
| maintainability. This is especially true in C++.
| zelphirkalt wrote:
| > This one really frustrates me. Write code to the complexity
| level needed to solve the problem, and nothing more. The only
| time I'd break from this is if I know for certain that the
| added complexity is going to be necessary in the near term.
|
| Mastery will be, when you write code in a way, that does not
| impose unwarranted limitations from the start, and still keep
| it readable and only containing mandatory complexity.
|
| Usually this can be achieved through deep understanding of
| the problem, mapping to simple concepts or finding or making
| that one concept that captures things well.
|
| Not always it can be done. Not always can a masterful
| solution be found, which keeps complexity low. However, it is
| definitely a mistake to draw a black and white picture of "if
| you want to make it work for the future, you must add
| complexity". Often people simply choose bad abstractions or
| wrong ones and will only realize, when the future has become
| the present and the system they built cannot fulfill some
| requirement.
| mcbishop wrote:
| > Often people simply choose bad abstractions or wrong
| ones...
|
| This is me. They tend to lead me to good abstractions
| (after merciless refactoring), and I'd like to think that
| I'm sucking less at this over time. But my overall process
| is very slow (good thing I'm self employed). Understood
| that it'd be better to stop and think instead of diving
| into new-abstraction boilerplate work.
|
| Worse though is to be under heavy pressure to ship and move
| on -- with the bad abstractions getting hopelessly
| calcified / buried.
| happimess wrote:
| > Often people simply choose bad abstractions or wrong ones
|
| When writing software, ideally I'd like to make all the
| right choices and use simple implementations of
| abstractions that do not impose unwarranted limitations.
|
| I think that it's sometimes worth it, early on, to do
| things the quick way despite bad abstractions. This can get
| you to a place where it's easier to reason about good
| abstractions.
|
| Sadly, I've been on teams where a bad abstraction was
| adopted because it was just assumed that that would be
| quicker. Instead of doing it the quick way, we just did it
| the bad way.
| wefarrell wrote:
| Personally 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.
| kristov wrote:
| I 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 wrote:
| Oh 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.
| btreesOfSpring wrote:
| This is a great point. Often I find situations when working
| with the subject matter experts where the code unveils edge
| cases in the business reasoning that the SMEs haven't
| considered. In many of those situations, the issue can be
| pedantic and the code can point to a catchall. Even so, it is
| funny how common these kinds of knowledge gaps appear when you
| are tasked with transforming assumptions into a functional
| working entity.
| martinflack wrote:
| This 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.
| larsonnn wrote:
| Set up your room like you would write your code and look if you
| are happy with that.
| dsieger wrote:
| Would be curious to know what strategies other people apply in
| order to keep complexity down over time!
| [deleted]
| jerf wrote:
| I 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.
| bbarn wrote:
| Trust 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 wrote:
| That's what we call 'scream testing'
| mystickphoenix wrote:
| For 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
| bcrosby95 wrote:
| Yeah, 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.
| nicwolff wrote:
| I 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 wrote:
| Prioritize functional testing over unit testing, which
| penalizes refactoring.
| nicoburns wrote:
| I'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 wrote:
| In 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
| b3morales wrote:
| Does 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.
| Chris_Newton wrote:
| Yes, 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.
| idontwantthis wrote:
| Unit 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.
| matheusmoreira wrote:
| My 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.
| [deleted]
| dasil003 wrote:
| Be 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.
| dsieger wrote:
| Absolutely! 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.
| marginalia_nu wrote:
| Unit 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 wrote:
| I 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 wrote:
| I 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
| idontwantthis wrote:
| I'm not sure if you're saying so, but those are not unit
| tests.
| nosianu wrote:
| Yes 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.
| adam_arthur wrote:
| Single 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
| plainnoodles wrote:
| I'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.
| ape4 wrote:
| Ah 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 wrote:
| Works quite well in conjunction with Googling "how do I do X in
| ssh stackoverflow".
| dannyobrien wrote:
| Wasn'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/
| mananaysiempre wrote:
| Also, 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
| morelisp wrote:
| > Rob Kernighan
|
| Is this a typo or a proposed Bourbakism?
| biorach wrote:
| The Unix philosophy is to do one thing and do it well. The
| ssh command wraps the large and complicated ssh protocol - I
| would say it does one complicated job and does it well.
| Joker_vD wrote:
| Never mind ssh(1), ls(1) pretty much uses all of [A-Za-z] as
| its options _plus_ -1, but still no -0 option. What I 'm
| personally really looking forward for is -2 option, whatever it
| would do.
| cassianoleal wrote:
| Did you mean `ssh --help`?
|
| `man ssh` gives me detailed descriptions of all flags.
| commandlinefan wrote:
| I can top that: `man read` gives:
|
| BASH_BUILTINS(1) General Commands Manual BASH_BUILTINS(1)
|
| NAME bash, :, ., [, alias, bg, bind, break, builtin, caller,
| cd, command, compgen, complete, compopt, continue, declare,
| dirs, disown, echo, enable, eval, exec, exit, export, false,
| fc, fg, getopts, hash, help, history, jobs, kill, let, local,
| logout, mapfile, popd, printf, pushd, pwd, read, readonly,
| return, set, shift, shopt, source, suspend, test, times, trap,
| true, type, typeset, ulimit, umask, unalias, unset, wait - bash
| built-in commands, see bash(1)
___________________________________________________________________
(page generated 2022-07-25 23:01 UTC)