[HN Gopher] Conventional wisdom as an anti-pattern
___________________________________________________________________
Conventional wisdom as an anti-pattern
Author : qudat
Score : 43 points
Date : 2023-02-27 13:49 UTC (9 hours ago)
(HTM) web link (bower.sh)
(TXT) w3m dump (bower.sh)
| jmull wrote:
| I don't think conventional wisdom is an anti-pattern, exactly.
|
| Better to think of it this way:
|
| It's a starting point, not an ending point.
|
| E.g., for DRY, if you notice yourself copying and pasting the
| same code around you might spend a few seconds thinking about
| whether there's some commonality worth capturing or abstraction
| worth building right now or not. The answer could be yes or no
| (for a variety of reasons), but it costs very little to reflect
| on it.
|
| (I'm not a fan of DRY though, as it's generally presented.
| Whether something is a good abstraction point or not doesn't
| really directly depend on how many times you're writing that
| code. WET, as presented here is even worse in this regard. If
| you're copying code around there's probably _some_ useful
| abstraction to make, but simply barfing out a utility function
| every time probably leads to a lot of bad abstractions. Code is
| expensive -- to write, test and maintain, so it 's good to
| minimize it. But abstractions are expensive too, since they
| directly affect the code written in terms of them. Bad
| abstractions don't end up making anything better, and probably
| make them worse since they will force you into a form of
| spaghetti code you might otherwise have avoided.)
| bsder wrote:
| > E.g., for DRY, if you notice yourself copying and pasting the
| same code around you might spend a few seconds thinking about
| whether there's some commonality worth capturing or abstraction
| worth building right now or not.
|
| DRY doesn't stop the kind of people who blindly copypasta,
| anyway.
|
| To me, DRY is more about avoiding desync. I should not define a
| constant in two different places--that will desync into a bug.
| I should not define a data structure in two different places--
| that will desync into a bug.
|
| A function, on the other hand, might actually want to desync
| after copypasta.
| aleksiy123 wrote:
| Pretty sure this is what DRY is supposed to be. Should be
| renamed really. If something needs to stay in sync it should
| be defined once.
|
| Things that looks sort of similar, don't necessarily need to
| be defined only once because its okay for them to diverge or
| desync and they probably eventually will.
| kazinator wrote:
| Problem is that the very definition of a cache is that you
| have the same thing in two (or more) places, which have to
| stay in sync. So blind adherence to DRY amounts to never
| using the technique of caching.
| aleksiy123 wrote:
| Its more a general guideline. In practice we break it all
| the time because it's not always possible or we may want
| to trade off for something else (performance in your
| case).
|
| Though generally I think when we talk about DRY in the
| code style sense vs the data (source of truth) sense
| these are related but different.
|
| However, even at Google there is a
| if_change_this_change_that lint rule to keep code changes
| between different files in sync. Meaning there's cases
| when you will have to define the same thing twice.
| kazinator wrote:
| If it's a general guideline, it reasonably extends to
| cover copy-paste code scenarios too.
| ozim wrote:
| Data structure has traps like - it has the same properties so
| it seems like something to DRY up. But if we add context that
| one of these is used on exchange of data with DB and the
| other on exchange data with frontend. Now we want these to to
| desync because there are valid reasons for that but at the
| start of project it seems that these have the same properties
| but again these are used in different contexts.
| a4isms wrote:
| I feel there are two separate things going on:
|
| First, the question of deduplication a/k/a normalization.
| Deduplication is cheap, and it's "a two-way door, not a one-way
| turnstile:" It's cheap to re-duplicate when you find that one
| of the users of your de-duplicated function or whatever needs
| new behaviour.
|
| Second, there is the question of the right versus wrong
| abstraction: If two different things use the same thing, that
| doesn't necessarily mean that they share the same abstraction
| conceptually. But of course, sometimes the duplication is a
| hint that there is some semantic commonality, and when you get
| this right, it's glorious, but if instead of de-duplicating we
| actually build the wrong abstraction, it can be extremely
| expensive to fix. That's more of a "one-way turnstile" problem.
|
| To me, when I see code that could be "DRY'd up," I have to ask
| myself whether it represents an opportunity to write a better
| abstraction, but I treat that decision conservatively, because
| of the cost of getting it wrong. In many cases, I choose to de-
| duplicate the code without creating what might be "the wrong
| abstraction," and it is often less expensive and lower risk to
| start there and only later consider abstractioneering.
|
| p.s. The tension between de-duplication and abstractioneering
| resembles the tension between is-a and has-a, often expressed
| by classical OO programmers as "inheritance" In those terms,
| I'm really describing my inclination as "Prefer composition to
| inheritance by default."
|
| p.p.s. Sandi Metz asserts that de-duplication is creating a new
| abstraction: https://sandimetz.com/blog/2016/1/20/the-wrong-
| abstraction. I feel that there is a sliding scale from "extract
| method" to "These two things share a superclass with a private
| message," but read her thoughts and come to your own
| conclusion.
| 0xbadcafebee wrote:
| Ahhhhh. This is what I come to HN for. Pseudointellectual
| contrarian thinkpieces by bored silicon valley people and the
| massive upvotes by other pseudointellectual contrarian bored
| silicon valley people debating the details of a ridiculous
| premise.
| xyzelement wrote:
| // Tests waste a lot of time
|
| Back when I was a developer, I had a manager who was super
| religious about unit tests (this seemed incongruous to me, I was
| a senior lead at the time and it was my vibe that such tests
| weren't what the project really needed the most at the time)
|
| The basic "problem" I have with tests is that - at least maybe in
| my experience - things tend to go wrong in cases that you _haven
| 't_ thought about so your tests don't cover them anyway.
|
| Like, what's the point of asserting that add(2,2) will yield 4 if
| you've already been thinking about that. And then, if you've
| already thought more complicated cases (eg negative numbers,
| cases that overflow) then your code already very likely handles
| them. And if you hadn't thought about them, your tests won't
| cover them either.
|
| I get it, it's cool to be able to run a test suite especially if
| I refactored something and be like "yup, add(2,2) is still 4!)
| but not sure how much actual value vs cost/annoyance I've seen
| from it.
| marcosdumay wrote:
| > what's the point of asserting that add(2,2) will yield 4 if
| you've already been thinking about that
|
| You are probably thinking about unit tests here, and yes, if it
| exercise a single point that you placed there by design, that
| test can only guarantee that somebody won't mess with it on the
| future.
|
| On the other hand, I'm writing an interpreter in a side
| project, and the single most useful test there is literally and
| assert that eval("1 1 +") equals eval("2"). That is because
| it's not a unit test, and it touches a lot of different points.
| But for an outside observer, it looks exactly the same as a
| useless unit test.
| PathOfEclipse wrote:
| I attribute some of the greatest successes of my career in part
| to having good unit test coverage. I have seen no other
| pragmatic way to solve the problem that, in the worst case, a
| minor software update can be O(N) expensive to make, where N is
| the size or complexity of your program. That little update has
| the habit of breaking things in code paths you'd never expect.
| With a good suite of unit tests, you can validate that your
| program likely still functions, and, far more importantly, you
| can do the validation _quickly_ , which results fast feedback
| loops and iteration times.
|
| Fast feedback loops are _essential_ to high developer
| productivity. When developers are being slow, in my experience
| it usually is related to the system they are working on having
| slow feedback cycles.
|
| > The basic "problem" I have with tests is that - at least
| maybe in my experience - things tend to go wrong in cases that
| you haven't thought about so your tests don't cover them
| anyway.
|
| You are right in that unit testing can't make up for bad
| engineering. One advantage of investing time in it, though, is
| it gives you potentially _more_ opportunity to think through
| those cases. And, as a consolation, prize, you write unit tests
| for bugs that make it through and now you have a great
| regression suite.
|
| Furthermore, a major issue that often comes when unit tests are
| omitted or not taken seriously, is that these tests can't
| easily be added after the fact, A system has to be designed
| with unit testing in mind, or else you end up with a bunch of
| untestable code. I believe that many legacy codebases remain
| without tests because the act of refactoring them enough to
| support any testing at all would be both time-consuming and
| high risk in terms of potential breakage.
| trgn wrote:
| I agree 100%, but just want to add my2c.
|
| Unit tests are in general never wasted, in the sense that
| they trap regressions before release. I don't think they
| necessarily are good to validate work though, or at least,
| they do so poorly. It's easy to write lots of tests which
| don't cover the areas your program will bork on in
| production. Which I think is point of GP.
|
| Value of tests compound over time. The longer the software is
| in production and maintained, the more work the unit tests
| have been doing. It could be a paltry 20% code coverage set
| of tests, but nonetheless they're fighting the good fight.
| The value of a test suite is expressed in the integral over
| time. Few tests can do a lot of work; on the flip side, a lot
| of tests are useless when they're continuously thrown away
| due to pivots in business logic.
| PathOfEclipse wrote:
| > I don't think they necessarily are good to validate work
| though, or at least, they do so poorly.
|
| For me, I've found they actually do help significantly to
| catch bugs even for my initial PRs. To know your code
| works, you either have to run it manually or else write a
| unit test, and you must do so for all relevant code paths.
| How much time do you save by avoiding the unit test, if any
| at all, and how long before you recoup that time via your
| integral? In some projects, I think the time saved is
| closed to zero, whereas the benefits accrue almost
| immediately.
|
| You are right that there is a tradeoff here, and I have
| been part of a project where I wrote a bunch of tests for
| code that ended up being a throwaway prototype. You could
| argue that I wasted time and resources there. On the other
| hand, I've seen more prototypes get shipped to production
| than scrapped, so maybe the calculated risk is worth it
| even in scrappier contexts.
| pjdesno wrote:
| Being able to automatically run your old tests to find
| regressions is super-super useful. I would argue that every bug
| you fix should turn into a test, since you're probably going to
| encounter it again.
| ozim wrote:
| You know that when you have testable code because you started
| writing it in such way - you might not have to write all
| failure modes right away into your test suit.
|
| Then when things go wrong in a way you haven't thought and your
| test did not cover for it - you can easily add test to cover
| that case.
|
| One of the workflows in testable code base is "find a bug" ->
| "write tests to cover the bug" -> "fix the bug" -> "tests
| passing" -> "your test suit now covers something that was
| actual bug and should not be ever again".
| dkarl wrote:
| > things tend to go wrong in cases that you haven't thought
| about so your tests don't cover them anyway
|
| I think this is a symptom of writing all the tests at the same
| time or immediately after the implementation.
|
| Adding test cases during implementation can be a good thing.
| You want to test cases that hit implementation edge cases, like
| when the length of the input is equal to, or one less or
| greater than, an internal buffer length or batch size. While
| you're coding the challenging parts of the implementation,
| these cases naturally pop out at you.
|
| However, while you're focusing on the hard parts, the "easy"
| parts fade into the background. You're much more likely to
| cover them if you make a quick list of test cases before you
| get immersed in the implementation. My experience is that when
| you (I) write test cases before implementation, I tend to cover
| more types of invalid inputs, as well as other conceptual
| corner cases, such as "degenerate" cases: zero or empty inputs,
| cases that have zero or empty outputs, and cases where an
| output could be computed in a straightforward way, but
| shouldn't be, because it wouldn't be valid.
|
| For example, one thing that has been a huge pet peeve for me my
| entire career is programmers treating empty inputs as invalid
| when there's a straightforward and correct way to handle them.
| After twenty years in the industry trying to get people to do
| this right, I've started to realize: people get this right if
| they talk about it _before_ they start the implementation! If
| you sort an empty list, the result is an empty list, no
| problem. If you search for an alphanumeric character in an
| empty string, the result is that it 's not found. But if they
| write the implementation before considering those cases, that's
| when they end up thinking it's okay to throw exceptions for
| them, because in their mind the implementation ("start with the
| first letter") becomes the specification.
| sitkack wrote:
| The system using the libraries should exercise those libraries
| sufficiently that integration tests catch those kinds of
| errors.
|
| If you are allowed only _one_ test, it should be an integration
| test that runs in under XX seconds so you can maintain flow and
| correctness.
|
| If a system is under constant churn, test at the interface
| boundaries, unit tests are further burden during refactoring.
|
| Unit tests ensure that the internals function in a certain
| manner, external users should be concerned with internals. Unit
| tests _are extremely_ useful in the core of a system like an
| evaluator, a rules engine, timezone, geospatial, computational
| geometry, etc. Your type system should capture many of the
| invariants in your system.
|
| I know what you are trying to convey, but the example
| `add(2,2)` encourages conversational confusion. If you are
| verifying hardware, that is a perfectly valid test, for a high
| level codebase, it is most likely a worthless test.
| jdblair wrote:
| > And then, if you've already thought more complicated cases
| (eg negative numbers, cases that overflow) then your code
| already very likely handles them. And if you hadn't thought
| about them, your tests won't cover them either.
|
| Good tests check boundary conditions and edge cases. They check
| errors conditions (like overflow), not just the happy path. And
| yes, they can be a pain to write, and they affect the way your
| code is written.
|
| On the other hand, a test suite with good coverage can give you
| tremendous confidence to make sweeping changes. I have a side
| project, an art project, and part of that is a custom on-the-
| wire networking protocol. I have tests that check all the edge
| cases I could think of. Did I miss something? Very probably. On
| the other hand, I've written the protocol 3 times now: once in
| python, once in C, and once in C++ (the C++ case was more like
| a giant re-factor of the C code to make it more usable), and
| I'll probably write it again in rust. Each time I had
| confidence I got it right because my tests pass.
| siva7 wrote:
| Tests waste a lot of time. That's certainly true for some
| teams. On the other hand, those tests have saved me in case a
| useful documentation is lacking which is virtually true for
| almost every closed source software project. To be fair,
| sometimes these tests were also misguided by misunderstood
| requirements, providing a false "documentation".
| pixl97 wrote:
| I'm not really a developer here, more of the wedge between the
| customer and developer when things go wrong, but don't these
| tests cover the continuous updating of the application as
| specifications change? Especially as multiple people are
| working on the code and may have different ideas of what's
| going on. May not catch everything, but can trigger a warning
| in many cases.
| crazygringo wrote:
| Exactly this. Also in codebases where the authors seemed
| opposed to writing good comments, tests are extremely useful
| in documenting expected behavior in edge cases, how an
| optional argument is supposed to be dealt with when missing,
| etc.
|
| Not to mention that people are often a lot less rigorous with
| "manual testing" than they should be. They don't think of
| scenarios, they skip scenarios, because nobody's checking on
| them. While when you write tests, you're creating a permanent
| record where you can be held accountable if you miss
| something.
| kraftman wrote:
| unit tests are comments that are forced to stay up to date
| and attached to the code they are for.
| pixl97 wrote:
| >They don't think of scenarios, they skip scenarios,
|
| Bane of my life.
|
| Me: "Hey, this big customer we're not going to tell to
| change their behaviors and pays us a lot of money is using
| this product in this particular way. We should put in a
| functional test case to ensure that we don't mess this up"
|
| Dev/QA: "Eh, no"
|
| This generally leads to a support call half a year later
| with the customer panicking, support groaning, and the
| developers panicking because they 'could have never
| predicted the customer using the application in this
| manner'.
| xyzelement wrote:
| Ah - I can see those kinds of tests as useful. I guess they
| are something like "integration tests" or something like that
| which tests the user-visible outputs of the system. My
| comment was about very low-level unit testing, where it's
| literally every function of code must be covered, _isolated_
| from other parts of code.
| justin_oaks wrote:
| > what's the point of asserting that add(2,2) will yield 4 if
| you've already been thinking about that
|
| There have been plenty of times when I have run a test like
| add(2,2) and I don't get 4. Usually it's a typo or some logic
| error rather than my "not thinking about it". Just like what
| was mentioned in the article, the alternative is manual
| testing.
|
| One benefit of testing is that then you start thinking about
| edge cases you didn't think about before. My common thought
| process: "What other tests should I add in here? What are the
| boundary conditions that are possible? Oh, running this
| function with an empty list even though it generally should
| have something in it. Huh, I didn't even account for that in my
| code!"
| ravenstine wrote:
| Tests can waste time because developers treat the test suite as
| its own application that needs to be managed.
|
| Stop. Doing. This.
|
| With some exceptions, tests should NOT be DRY. They should be
| dumb, explicit, repetitive, conventional, and as independent as
| possible. This allows tests to be more quickly understood by
| any given developer and changed without breaking other tests
| for stupid reasons unrelated to the actual application.
|
| So much of my time has been wasted at every job I've had
| because developers believed that the tests should be really
| clever and use a bunch of magic and shared logic to "save
| time", which in reality becomes difficult to manage and
| understand. Most tests are crap for that reason alone.
| dwheeler wrote:
| _Testing_ is important, because there are many ways things can
| go wrong.
|
| _Automated_ testing is critically important, because "manual
| testing" is often another phrase for "testing we plan to do but
| won't do often if ever".
|
| _Unit_ testing may or may not be the best approach. Unit tests
| are fast, and make it easy to answer very specific answers, but
| they 're tied to the specific software design. Since computers
| have become much faster, I generally recommend that people use
| more integration testing, because they reveal when the
| components fail to work together (for whatever reason), and
| their added CPU effort is typically no big deal.
|
| That said, I agree that testing is only useful if it's
| plausible it would detect a problem. But developers are really
| good at asserting something "can't happen" and then does :-).
| alexpetralia wrote:
| You should basically follow conventional wisdom if you are doing
| conventional things, and follow unconventional wisdom if you are
| doing unconventional things.
| Jtsummers wrote:
| Manual testing is for chumps (the US government is a good source
| of chumps). It's a great way to stretch a project out and balloon
| the budget because you need a separate team (maybe even multiple
| teams) to do in a few weeks or months what could have been done
| in a few hours or days by a couple computers. Why anyone would
| prefer manual testing for anything is beyond me, it's a great
| source of errors because it becomes your big time crunch in large
| projects. You end up cutting out tests because "that one never
| fails" (until you cut it) and rushing through the tests
| handwaving issues with "I'm sure we just fat-fingered it".
|
| Automated testing takes less time, fewer people, less hardware
| (normally, but not always), and can be kicked off by _anyone_ in
| the project team (if you 're sane about your project management)
| at any time. So you can run your suite today and know (worst case
| on huge projects) by Friday or next week that it's actually
| passing or failing. If you stick with manual testing, you won't
| know about failures for weeks, months, and sometimes years.
| Genius if you're going for government money, moronic if you're
| spending your own.
| logicalmonster wrote:
| > Why anyone would prefer manual testing for anything is beyond
| me,
|
| Formal software testing is great, particularly if you're
| working on a hard problem with clear goals like building some
| hypothetical API to do some financial calculations or
| transactions where an error might cost tens of thousands of
| dollars or more.
|
| But there's some cases where literal manual testing is the best
| business decision, even if it's not the best software
| engineering decision.
|
| To give one example: have you ever worked in an agency
| environment?
|
| What do clients do when you hand them off any scale of project?
| Some people within their organization will then go over every
| inch of the software and see if it works as they expected. They
| will literally never ask once what your test coverage is. But
| they will notice if some test data they input themselves
| doesn't give the right answer, or if some minor UI element they
| never really thought through doesn't seem to work the way they
| like.
|
| Writing tests in this environment is 99% intellectual
| masturbation.
| kazinator wrote:
| > _But they will notice if some test data they input
| themselves doesn 't give the right answer, or if some minor
| UI element they never really thought through doesn't seem to
| work the way they like._
|
| Those people are going to not stop at one; so you will be
| foolish if you don't write a regression test for the behavior
| they wanted.
|
| When they find the next thing they think is broken, and you
| try to fix it, you could regress your earlier fix for the
| first item.
|
| In reacting to the user reports, you may be breaking other
| things that are not tested, but at least you pin down the
| behavior that the users are reporting.
|
| UI element not working right in some way could be a genuine
| "pass" for testing. I mean, writing some monkey test that
| feeds events into a UI to check that it's in exactly some
| expected state could be a big waste of time.
|
| Probably if you were writing a reusable widget framework, you
| might want that sort of testing, because you could make an
| inadvertant change which makes every instance of some widget
| behave differently in downstream applications.
|
| If you have some complex behavior in your UI that doesn't
| come from the underlying widgets, and has to be right in
| certain ways, then that could be worth testing.
| MetaWhirledPeas wrote:
| When Barry from HR doesn't like the way a page works, that's
| a feature request, or maybe it's a bug.
|
| When a bit of errant code refunds the customer $10000 instead
| of $100, that's a BUG (ALL CAPS).
|
| The two are not mutually exclusive. You need human critics
| looking at what you create, but you also need objective
| tests, and automation is very good at objective tests. You
| might even ask Barry to look at your creation earlier,
| further reducing the need to inject more humans than you
| already have.
| Jtsummers wrote:
| Your comment was dead when I saw it so I vouched for it.
|
| > But there's some cases where literal manual testing is the
| best business decision
|
| I will agree, manual testing can be the best business
| decision. Especially if it's someone else's money and time
| you're spending on it rather than your own. Or if your system
| is trivial or short-lived.
|
| I don't work on short-lived systems, and manual testing has
| _repeatedly_ been a major hurdle to improvement overall when
| dealing with non-trivial, long-lived systems in my
| experience. Manual testing either misses too much, or takes
| too much time if it 's comprehensive. And even if it is
| comprehensive, or perhaps _especially_ if it 's
| comprehensive, manual testing gives you many false positives
| and negatives because it is error prone.
| logicalmonster wrote:
| There's many different worlds out there.
|
| I think most of the developers on HN have predominately
| worked on long-lived products where some culture of heavily
| testing things makes perfect sense and the idea of doing
| anything else seems strange. If you've got a customer base
| on some SaaS that depends on things working as expected,
| then yeah, having lots of tests to make sure that your
| system is consistent and changes on various edge cases
| spread over the course of years don't break subtle things
| is vital.
|
| So there's probably sort of a developer cultural disconnect
| when somebody who has often built urgently needed, shorter-
| life, bespoke projects points out that there's some types
| of projects and environments where software testing brings
| a much different value-proposition. In some cases, you're
| not building something that's intended to last for years:
| maybe you're building a specific software that's intended
| to be used in a booth demonstration for 3 days in one
| industry conference, as an example. In some cases, the
| speed of development and an absolute lowest cost takes
| priority over perfect software engineering principles, or
| otherwise something couldn't even be built.
| qudat wrote:
| > Automated testing takes less time
|
| Hmm, this has not been my experience, but I spend a lot of time
| in the UI. Writing UI tests is a massive pain, the functions
| are impure, naturally deal with side-effects, and involve robo-
| interacting with a fake browser. The amount of code required to
| simply scaffold a fake browser that works reasonably is a
| monumental feat and that's before you start writing a single
| test.
|
| Further, it's not enough to measure how long it takes to write
| a test but also to maintain them. That's the time creep that
| can be absolutely brutal.
| ozim wrote:
| Just throwing opposite view. I saw money burned on test
| automation because manager needed to check the box. They got a
| guy that set some automation up in 6 months and then he left.
| Guy did not know the project and project was evolving in rapid
| pace so none of the GUI automation was working couple of weeks
| after he left - actually a lot of stuff stopped working while
| he was on it because project was moving under his feet.
| Jtsummers wrote:
| > Guy did not know the project
|
| That's the bigger issue. Why would you put someone in charge
| of testing something who doesn't know it? That's brainless.
| Your manager was not very competent, sorry you experienced
| that.
| DrewADesign wrote:
| When people use meaningful guiding principles dogmatically, the
| principles aren't the problem.
|
| As a chef, I often adopted the "measure twice, cut once"
| principle from carpentry. Saved big batches of sauce or whatever
| several times during prep. Working the line on Saturday night,
| however, doing that would cost someone their job within half an
| hour.
|
| Don't replace problem-solving with rules and always use the right
| tool for the job. If you realize you aren't, it's usually a lot
| easier to switch the tool than modify the job.
| kazinator wrote:
| The "convention over configuration" can be regarded as self-
| referential. These guidelines are often good default choices in
| situation where you don't have a strong reason to go either way.
| You wouldn't write a program which is configurable between those
| choices (e.g. exhibits more code repetition or less based on a
| run-time setting): so you go with a good default convention. If
| you can avoid repeating yourself, that's usually good; machines
| should do the repetitive work rather than people. If you know
| that two or more repetitions of something are only initially that
| way and soon going to diverge, then might as well fork those
| copies now. Or maybe do allow yourself to repeat yourself, but
| via macro. Some compiler optimizations violate DRY by design:
| function inlining, and loop unrolling. It's invisible to humans
| who aren't disassembling the output, or measuring code size
| changes.
| pjdesno wrote:
| I'm not sure the opposite of "configuration" should be called
| "convention" - the worst abuses I've seen of punting to user
| configuration have been ones where the best solution was only
| determinable at runtime. (e.g. compare user-configured fixed
| window sizes in the doomed ISO protocol stack with dynamic
| window control in TCP)
|
| Typically the user _doesn 't_ know best - they copied a config
| that worked for someone else, years ago on a different machine
| and workload, and don't know what any of the parameters
| actually mean. In the worst case (sendmail?) you have O(0)
| people who actually know how to use the configuration language,
| and 10 competing higher-level config generators.
| __derek__ wrote:
| Right. They're razors, not commandments. Given a choice between
| two reasonable alternatives, it might be wise to pick the one
| that's {unrepetitive|simple|explicit|extensible|testable}.
| kunalgupta wrote:
| Luv manual tests. With everything that ships I bullet point a
| list of tests that theoretically would be written. Then I test
| the most important ones by hand. I've never had to retest yet,
| but theoretically if something was wrong i can start debugging by
| poking at these tests. If something broke at all i could even
| automate that specific test
| poszlem wrote:
| You know what's not given enough credit? The fact that those so-
| called "patterns" are really just trendy ideas pretending to be
| "knowledge". It's like fashion - it goes in and out of style. Now
| we have all these blog posts saying "DRY considered harmful", but
| back in the day, everyone was all about "DRY considered good".
| It's just a trendy thing, man. And all this backlash against it?
| Yup, you guessed it - it's just another trend (because it's not
| cool to follow the rules anymore).
|
| To be real, most of those ideas probably have some truth to them,
| but like always, it's about weighing the pros and cons. It's all
| about those tradeoffs.
| esailija wrote:
| There is a lot of truth to DRY but it has been distorted by
| becoming completely obsessed with it as if it was some end to
| reach, rather than any other property with its own tradeoffs.
|
| And its not just developers, but even some companies are also
| obsessed with removing "waste" by sacrificing everything just
| to minimize any kind of seemingly duplicated effort without
| ever considering if it's actually cheaper to do so..
|
| When DRY is shown as high ideal somewhere, how often do they
| also mention the tradeoffs like tight coupling and premature
| abstraction?
| tonymet wrote:
| People are confusing wisdom and rules. It takes wisdom and
| experience to understand "DRY" , but that doesn't mean people
| should DRY in every possible case. Following a rule blindly isn't
| wise. The Wisdom is in the understanding of why the rule matters
| along with when & how it should be applied.
| muyuu wrote:
| assuming conventional wisdom to be an anti-pattern is an
| exceedingly common anti-pattern
| malcolmstill wrote:
| > If a test never fails, is it a good test? If I have to change a
| test everytime I change the implementation, is it a good test?
| Writing good tests is really difficult.
|
| If you don't like writing / maintaining tests or don't have the
| time, let the computer write them for you! [0][1]
|
| [0] https://insta.rs/ [1] https://vitest.dev/guide/snapshot.html
| EGreg wrote:
| I don't write automated tests and none of my developers do,
| except the guy building smart contracts. And yet I am not sure I
| am doing the right thing. We only did it because we couldn't
| afford the extra time and money to do it.
|
| _If a test never fails, is it a good test? If I have to change a
| test everytime I change the implementation, is it a good test?
| Writing good tests is really difficult._
|
| Yes -- just like good interface is not noticed, or how good
| security which prevents issues isn't noticed when things go well.
| justin_oaks wrote:
| I had a hard time writing tests because nobody else around me
| did it. Nobody taught me how to implement good tests or
| implement testable code. I had to figure it out by myself
| through trial and error, and through reading articles and
| watching videos.
|
| Ultimately, I found writing tests were immensely helpful for
| the work I was doing. I was writing the API backend for
| desktop/mobile apps to call. When one of the guys working on
| the client apps would claim my stuff wasn't working right, I
| demanded an example HTTP request that I could use to reproduce
| the problem.
|
| Most of the time they never got back to me. Often the problem
| was with their (untested) code and they wanted to throw the
| problem over the wall for someone else to investigate. I had
| the confidence that my code was behaving well because of the
| tests. So when someone else came over and said "Your stuff is
| broken" I had the confidence to say "Prove it".
|
| With that said, I recognize that there are cases that are lower
| stakes that may not require such thorough testing. Right now I
| have some APIs I maintain that I don't write tests for. These
| APIs are of the type where if there's a bug then the user of
| the API says "Hey, it's broken" and I say "Ok, I'll fix it in a
| couple days" and he says "Sure, let me know when it's done".
| This is quite different than the API that gets called hundreds
| of times a second and customers are screaming "I want my data
| now!"
| Veuxdo wrote:
| > We are a hammer and every problem is a nail that can be solved
| by the act of thinking. We fall into the trap of overthinking
| every scenario because we are paid to poke holes in systems and
| then patch them before it gets to the user.
|
| I was thinking about this yesterday, but more in terms of how
| small teams (or solo devs like in my case) can out-perform larger
| teams just by not even considering every edge-case.
|
| I've discovered these overlooked cases later and been kind of
| embarrassed, but also happy that I _didn 't_ catch them right
| away, since fixing them wouldn't have been worth it, and the urge
| to fix them would have been high.
| qudat wrote:
| Exactly! I try to be intentional about what edge-cases I care
| about and maybe more importantly the ones I don't care about --
| for now. I also try to be intentional about time-boxing how
| long I spend thinking about edge-cases. Again I think context
| is important here.
|
| I think this overall mentality is the contrarian in me
| resisting the status quo.
| taeric wrote:
| Hubris is an amazingly effective tool that more managers and
| senior engineers could leverage from junior members.
|
| There are obvious problems and pitfalls that one needs to look
| out for. But all too often companies and teams grow to a size
| where their main product no longer has a place for people to
| try otherwise bad ideas.
| justin_oaks wrote:
| When I find edge cases that are low-stakes and unlikely to
| occur, I usually just ignore them completely. If they do occur,
| and someone thinks I shouldn't have allowed that edge case to
| go unhandled then I have plausible deniability of "I didn't
| think of that edge case".
|
| I guess I'm fortunate enough not to have the high "urge to fix
| them".
| planede wrote:
| It's easy to fall into the trap of introducing even more edge
| cases by handling some other edge cases.
|
| The correct way to handle edge cases is to not have them in the
| design. Easier said than done. And sometimes the edge cases are
| in the spec. Sometimes they are justified, but often they are
| not. These should be eliminated before the spec is finalized.
| kunalgupta wrote:
| I find it useful to write a list of the tests you would write
| if you were writing tests. Then you have made a list of all the
| things that can go wrong, instead of being caught off guard if
| it breaks
| esailija wrote:
| Yeah I admit I have wasted too much time solving edge cases
| that either never happen or happen but the effect is not so big
| of a deal that it was worth to handle it.
| GMoromisato wrote:
| You cannot reduce software engineering to an algorithm[1],
| because if you could, you could (duh) write a program to automate
| it and not have to worry about it.
|
| That's why the job is so hard: you have to think about the
| specific case and decide the best course of action. Sometimes
| (maybe often), DRY is the right solution; sometimes not. Same for
| all the other "rules" of software engineering.
|
| I think the OP is just reinforcing this: don't go on autopilot--
| think!
|
| [1] Obligatory disclaimer: Eventually AGI will reduce software
| engineering to an algorithm, but not today.
| kposehn wrote:
| Conventional Wisdom is just Convenient Assumption in experience.
| We need to regularly check our assumptions, lest we find
| ourselves operating at right angles to reality.
| codeulike wrote:
| _I 've written so many automated tests, but how many have
| actually caught something important? A couple dozen?_
|
| Yeah I'm glad TDD isn't the dominant religion any more. If you
| write a test and it never fails throughout the lifetime of the
| project, thats wasted time.
| moron4hire wrote:
| The last time I found TDD to be useful was when I was
| implementing some code for processing of data feeds according
| to a pretty well-defined specification. The spec translated to
| tests quite easily and then the tests very readily pointed out
| where the data did not match the spec 100%. Luckily, we were in
| a position to modify the data-generating side of things, too,
| to make sure everything was in-spec.
|
| But most of the time, I'm dealing with user interfaces, service
| integrations, and data munging. I have "test benches" instead
| of unit tests for these. They're like "minimal reproductions",
| fully stand-alone programs that exercise the code in the
| "expected" way. The big issue is that the "expected way" could
| change at practically any minute, depending on business need.
| There's no "spec" other than, "are people's expectations for a
| usable, comfortable system being met?" We _want_ them to be
| changeable at the drop of a hat. And those test benches help
| make those changes possible, by not having to work in the full
| UI to change the behavior of one control, one service, etc.
| zcmack wrote:
| i think TDD is a great method, but not dogma. likewise with
| code coverage. being prescriptive about one or the other just
| leads to angry people who get fed up with the principle and
| rebel.
| dllthomas wrote:
| I want to push back on this in two ways.
|
| First, it's my understanding (as someone who usually doesn't
| TDD and hasn't read/thought that deeply about it in a while)
| that the core of TDD (vs other testing doctrines) was to
| iterate by making a _failing_ test, then making it pass. If
| that 's correct, then if you're writing tests that never fail
| throughout the lifetime of the project then you're not doing
| TDD, though culture does weird things to practices and
| definitions and I'm sure there's places that called any damned
| thing TDD when it had more hype.
|
| Second, I think a test (or static check) that never fails can
| be useful if it lets the programmer pay less attention to
| making sure the program is correct in that particular way
| (freeing up bandwidth for other important considerations), and
| as a communication tool demonstrating that the system does in
| fact work the way it does. Whether those benefits are worth it
| depends on a bunch of features of the particular context,
| including how long it takes to write/maintain the test
| (although if it never fails then at least you probably don't
| need to update it) and how long it takes to run the test.
| codeulike wrote:
| _the core of TDD was to iterate by making a failing test,
| then making it pass_
|
| Yes and when I first heard that 15 years ago (or whenever it
| was) it sounded like genius and I did it on a few projects.
|
| Its a useful crutch maybe if you're doing something very
| unfamiliar
|
| But as a dogma that must always be followed? And having a
| test for every single 'specification' and edge case? No -
| screw that, you end up faffing around for ages setting up
| mocks and testing stuff that doesnt deserve it. And you end
| up with a massive library of tests that get on your nerves.
|
| Also everything got completely bent out of shape for a few
| years because everything had to be injectable and testable.
|
| https://dhh.dk/2014/tdd-is-dead-long-live-testing.html
|
| http://web.archive.org/web/20180215225218/https://iansommerv.
| ..
|
| http://blog.cleancoder.com/uncle-
| bob/2016/03/19/GivingUpOnTD...
|
| https://rbcs-us.com/documents/Why-Most-Unit-Testing-is-
| Waste...
| qudat wrote:
| > If that's correct, then if you're writing tests that never
| fail throughout the lifetime of the project then you're not
| doing TDD
|
| Agreed.
|
| > [tests that never fail can be useful] as a communication
| tool demonstrating that the system does in fact work the way
| it does.
|
| Agreed as well. That's what makes the section I wrote on
| testing sting for me. I read about all the benefits and see
| how it can be helpful as a form of documentation. I see all
| the upsides but at the end of the day, we still need to
| calculate if automated tests are really worth it. The
| maintenance burden on them can be huge. A couple line change
| and confirming it works with a manual test can take a couple
| of minutes, but fixing an integration test that is now
| failing can take the entire day or worse.
|
| It's tough, but my main goal was to challenge the dogma of
| "always write automated tests."
| justin_oaks wrote:
| > It's tough, but my main goal was to challenge the dogma
| of "always write automated tests."
|
| It's perfectly fine to challenge dogma and ask people
| figure out whether the automated tests make sense for their
| use case. I think the push for automated tests came when
| there were so many others who grossly underestimate the
| benefits of automated tests and dismiss them off-hand.
|
| It's hard to see the benefits of automated testing. It's
| easy to see the time spent writing the tests while it's
| hard to see the time saved by catching bugs before they
| happen. It's also easier to refactor if you have tests to
| confirm that your refactoring didn't break something.
| dllthomas wrote:
| Right, just because it has value doesn't mean it has enough
| value. My objection to the parent (not the article) was
| that it wound up dismissing certain kinds of value.
| kazinator wrote:
| Just like if you wear a seatbelt all your life, but never have
| an accident, what a waste of effort. Not to mention $$$ spent
| on car insurance.
|
| Programmers should peer into their crystal balls and predict
| which code is going to need to be maintained in ways that will
| break it and only write regression tests for that. For all the
| rest, just poke at it manually in your REPL and debugger and
| call it tested.
|
| You heard it here.
| codeulike wrote:
| Yes, I'll write a test for something that I know from
| experience is likely to give me trouble later on with
| regression problems
|
| Writing a test when you find a bug and then fixing the bug is
| also quite handy sometimes
|
| And I'll write tests upfront when there's well defined
| outcomes and I know the code is going to be tricky
|
| But I'm not going to write tests for dumb stuff that I know
| wont break
| taeric wrote:
| A problem with calling it wasted time, is that implies you
| could have recaptured that time in some other, more productive,
| way. And, I'd wager odds are high that you could not have.
| acmegeek wrote:
| When it comes to "conventional" wisdom, I think there's simply a
| confusion between methods and principles. Am sure many here have
| heard the quote by Harrington Emerson[0] that roughly goes:
| "Methods are many, but principles are few. Methods always change.
| Principles never do."
|
| So I think that many things, like DRY and KISS, are really simply
| principles that were distilled after many attempted much and
| learned a lot. Things go sideways quickly when these are mistaken
| for methods.
|
| Whenever some rule or method doesn't really seem to fit, I wonder
| if this is really more of a principle to help guide me to the
| most contextually appropriate method.
|
| So, don't necessarily throw out DRY and similar, just treat them
| as guiding principles that will absolutely make sense in the
| right situations.
|
| The above quote is the most commonly known form, and is often
| misattributed to Ralph Waldo Emerson. Here is the full original
| quote: [1] "As to methods there may be a
| million and then some, but principles are few. The man who grasps
| principles can successfully select his own methods. The man who
| tries methods, ignoring principles, is sure to have trouble."
|
| [0] https://en.wikiquote.org/wiki/Harrington_Emerson
|
| [1] https://www.goodreads.com/quotes/346365-as-to-methods-
| there-...
___________________________________________________________________
(page generated 2023-02-27 23:02 UTC)