9 ms·
Test && commit || revert
- zacherates 8y ago... except seeing the test fail is how I know my test might be working. It's trivial to write passing tests.
- groestl 8y agoThat's the thing, if my test is green on the first try, I assume something is horribly horribly wrong..
- jrockway 8y agoIndeed. I always have to change a condition in the test to make sure that it's actually running, rather than that I just happened to get it right the first time ;)
- l0b0 8y agoThat's why the red-green-refactor style of TDD is so useful. You start by writing a failing test, then make it pass with the simplest possible change, and then refactor the code while keeping everything passing. You'd be hard pressed to write a test which fails and then passes by accident. It leads to extremely clean code IME.
- mlthoughts2018 8y agoIn my experience this approach offers nothing, because the whole challenge is in writing the right tests, and you almost never know what the test is supposed to be like until after writing code for a while. There’s a symbiosis between knowing what test needs to be written and knowing what code needs to be written. No part of specs, TDD, BDD, etc., actually ever captures the reality of this, despite every Name Brand Workflow always claiming to.
- l0b0 8y ago> There’s a symbiosis between knowing what test needs to be written and knowing what code needs to be written. This should only be the case while unfamiliar with the language or project at hand. In my experience, once you're familiar with the project and the language, mulling over a requirement for a short time should be enough to split it into smaller features which are simple to test in isolation.
- mlthoughts2018 8y agoIt has pretty much nothing to do with your experience level, your familiarity with the project or the language or tooling. When those things matter, they amplify this phenomenon, but the baseline effect is already high. It’s a function of requirements being ambiguous by design. Non-engineering stakeholders have a severe need for requirements and specs to be fungible and ambiguous, such that whether or not the state of the project satisfies requirements is fundamentally not codified in any objective document and no test can reflect useful objective measures of it. Tests can only loosely approximate it and the ways to do so are always shifting and changing and up to a subjective interpretation of some non-technical people. Perhaps the only objective measure that non-tech people will agree to be specific about is financial cost, and even then usually only after it becomes a problem. That’s every project in a product-oriented tech company period, and anyone claiming otherwise is full of it. There can be isolated, tiny pockets of maintenance work or last stages dev work once you pass a point of no return when business people can no longer sustainable lobby for requirements to continue to be ambiguous and fungible, and finally you can start proving things with accurately scoped tests, but it’s so late in the game that the idea you could do something like TDD at that point is comical. Instead, becoming a good product-oriented developer means in part learning how to write well factored tests on the fly at the same time you are writing the code to be tested, and keeping testing code clean and set up with fixtures and set up to run in highly automated ways, so that when you’re inevitably ripping stuff out of the implementation for the 18th time due to yet another priority pivot from sales, you can nimbly also update the tests as you go.
- l0b0 8y ago
- eterm 8y agoI feel this comes from a frustrating kind of purism, like xp, which is a nice thought experiment but is completely unworkable in the real world. The real-world consequence of this would be people not writing tests until after they are feature-complete so they don't risk losing work.
- ryanisnan 8y agoErm, what about test coverage? You could prevent committing untested code.
- henryfjordan 8y agoYou could couple this with code-coverage requirements, like coverage must always increase or stay level with every commit. That can still be gamed but at least it's harder and more obvious to other devs
- groestl 8y agoPeople would just write tests which can never fail, and (in the best case) add the assertions in the end, one by one.
- was_boring 8y agoSpeaking of tests that never fail... I've seen contractors hand over code exclaiming they've met the 75% test coverage required by SalesForce's platform but never actually asserting anything.
- chowells 8y agoOne release of the GHC Haskell compiler, long ago, had a bug where it put all your source files in the list of build artifacts it had created. When GHC ran into a type error, it deleted all its build artifacts from the current run. There are stories told that a few argued to keep it around as a feature for training in doing mental type checking.
- jb3689 8y agoThank god for buffers :)
- jessaustin 8y agoMy habit of CTRL-Z'ing out of multiple running vims has saved the day a few times.
- jasonm23 8y agoYou can easily time travel in Vim. https://coderwall.com/p/twr_bw/time-traveling-in-vim https://coderwall.com/p/twr_bw/time-traveling-in-vim
- jessaustin 8y agoSure, but what I meant was that a deleted file that's still in a vim buffer may be rewritten.
- hnruss 8y agoThe author describes the script in https://medium.com/@kentbeck_7670/limbo-on-the-cheap-e4cfae840330 https://medium.com/@kentbeck_7670/limbo-on-the-cheap-e4cfae8... "test && git commit -am working" This seems like a recipe for a useless commit history.
- tzar 8y agoProbably useless in terms of existing, standard development processes, but it seems to me like this Limbo effort is to come up with a new one (in which we can assume, by the principle of charity, that this is not entirely useless).
- Rychard 8y agoI assume that commits are being made to some sort of topical branch (Edit: I guess not? That seems like a poor decision). Personally, I encourage the developers across our organization to do an interactive rebase prior to pushing to a remote, to "curate" their commits (squash/fixup). This greatly improves the utility of the commit history, at the expense of losing the actual commit history as work progressed: - FeatureA should ensure valid user session - fix test - missing semicolon - null check - fix incompatibility with FeatureB - shit broke the build - address PR comments
- derefr 8y agoThe workflow is using Git as a realtime state-synchronization primitive (like Operational Transformations in Google Docs), not an SCM. The point is to push the commits over to the other person as soon as they're created, and for the other person to pull them and rebase against them as soon as they're created. Unlike character-by-character OT sync, though, each synced Git commit does represent a working codebase. You could theoretically retroactively grab huge numbers of these commits and squash them down and describe the result—though multiple independent things might have happened in them that would be very difficult to untangle, given that, in this workflow, it's entirely possible that two separate people are working collaboratively on two separate features, in the same file, at the same time.
- arithma 8y agoThis sounds like a sure way to enforce a local optimum. If you can't make mistakes and break enough things at a time to change the architecture, in order to fix the architecture itself, it means your architecture is going to be extremely sticky, and there will be no chance that design mistakes will be corrected.
- jasonpeacock 8y agoOwning production systems means you can't make mistakes and break things. People are already doing what you're saying can't be done, everyday, when they change production systems. He's saying you should figure out the path from A to B and get there in small steps. And make sure your tests pass at each step. Don't make big changes that are hard to propagate and merge with others' changes. Basically, avoid the classic "nobody change anything, I'm doing a merge today!" situation.
- lifthrasiir 8y ago> People are already doing what you're saying can't be done, everyday, when they change production systems. Only seemingly so. People do break tests when they are writing (or removing) code, and even intermittent commits often don't pass tests (they thus have to be squashed before the merger, they only exist for making reviewer's jobs easy and keeping them risks faulty bisect for example). Development and deployment are separate things.
- l0b0 8y agoWe did this in CI at an earlier job, and what we found is that it works if and only if you have a good test suite. In our case we had too many false positives because of badly written legacy tests, so it was not a usable approach.
- jrockway 8y agoDo people not typically modify the tests in the change where they introduce a feature? I have never worked anywhere where I would check in a change without the tests passing. If you are making a huge refactoring, you do it a step at a time, with each incremental change still having passing tests. When I was at Google I believe that changes were automatically rolled back if they broke enough tests globally. I don't recall anyone ever complaining except that the rollback was slower than they would have preferred. You simply can't check in a library change that breaks half the company, after all. If people are writing adequate tests and consider commits to be "change lists" instead of just a random thing they want to back up from their local machine... this is a no-brainer. But it does require the culture of having your code reviewer check that they think there's enough code coverage. Otherwise "passing tests" doesn't mean anything. (Google's tools happily showed lines of code that were covered by tests in the review UI, so you have objective and subjective data as a code reviewer to look at before approving.)
- jonstewart 8y agoYeah, this is how I’ve worked for 16 years. The unit tests always pass. Maybe once or twice a year you can go way out on a limb and do something exploratory where you don’t care about the tests passing for every commit, but... part of TDD and CI is that the tests always pass. In some ways, I think the feature branch->PR model of today is a step backwards from traditional central server single-trunk development of years gone by (CVS/Perforce/SVN). Branches and PRs work better for F/OSS development, and for monster refactoring, but I kinda long for the days of a normal team just committing to master/trunk several times a day.
- jrockway 8y agoI think PRs are logically the same as Perforce changelists. The fact that you can pick apart a PR into multiple commits does pollute the history when you merge, though, unless your tools treat the PR as a single atomic commit (for bisection, etc.) I have never treated the individual commits inside a PR as anything other than "I'd like to backup my workstation to the cloud now", but if people are treating them as something meaningful, I think that's a mistake.
- boffinism 8y agoTest && (commit || revert)? Or (test && commit) || revert?
- CGamesPlay 8y agoAssuming it's shell, they are always processed in left-to-right order, so it's (A && B) || C, or (A || B) && C, with no proper order of operations. In this case, it's closer to test ? commit : revert though since the commit can't fail (but that's not a bash operation)
- Spivak 8y agotest && commit || revert is grouped like test × commit + revert so it would be the latter.
- kazinator 8y agoIn C and derived languages, this is true; not in the POSIX shell, though, where these are on the same precedence level, and associate left to right. $ echo 4 || echo 5 4 $ echo 4 || false && echo 5 4 5 This is because the grouping is (echo 4 || false) && echo 5.
- adrianmonk 8y agoI'm thinking they mean the Bourne(-style) shell, where this syntax means that test is run, either commit or revert is run. That is, it is a shorthand for: if test then commit else revert fi Presumably the point is to encourage you to keep your code changes and tests so small that you are OK with losing work.
- mikelward 8y agoIt's actually equivalent to if test; then commit || revert else revert fi Or in another language: try { test commit } catch { revert } This is because test && commit is evaluated, and the resulting value is the left side of expr || revert Not sure if that was intentional, but it's a very nice one liner assuming either your commit never fails, or your commit runs extra tests, and you want to revert if those fail.
- thechao 8y agoThe is how I’ve developed code for almost a decade. For a given test we have the following behavior triple: desired result, expected result, and observed result. A test fails if it’s expected result differs from the observed result. The set of development work is every place the desired & expected results differ. The CI is passing when no test changes behavior. There’s a revert “window” where a change-in-state can be blessed.
- Something1234 8y agoWhat's the difference between a desired and expected result?
- thechao 8y agoDesired is what you want to happen, expected is what you expect to happen, and observed is what happened with the current run.
- XorNot 8y agoFrom the perspective of a test that only passes or fails, this doesn't gain you anything. I suppose we could imagine a test system which includes more metadata, and allows for placeholder values to let tests pass while producing an annotation that the product is not production ready.
- zahrc 8y agoWinning lottery is your desired result. Not winning it is your expected result.
- city41 8y agoWhat am I missing? If commit is a possible action, then we have a unit of code not yet in the repo. So if that is the case, won't the revert be reverting a previous, unrelated commit? If we can't commit unless the tests pass, then why would we ever revert? I believe what I'm missing is taking the word `revert` too literally. I suspect it's actually `test && commit || checkout .` essentially.
- admax88q 8y agoYou're thinking in terms of git. Most version systems before used revert to mean undo local changes, not apply the reverse of a commit.
- Tyr42 8y agoIt's not git, I think revert means "revert to state at last commit"
- derefr 8y agoIt's: ./test.sh && (git add . && git commit -m "auto") || (git reset --hard HEAD)
- OJFord 8y agoI don't think the contrast to TDD (made in the image more than the text) is right: why not split 'add test & pass' into 'add test' & 'pass', and then you still have TDD. Sure, you have one commit where the test was broken. But the product was broken anyway, and now you have a commit proving the test works.
- LukeShu 8y agoI don't like lumping "add test & pass" together. It's very easy to accidentally write a test that doesn't test what you think it tests, and actually passes even without the "fix" applied. I do think it's very important to verify that the test fails without the associated change applied. This breaks `git bisect`, so maybe they shouldn't be separate commits in the public commit log, but they should be separate commits while you're working on it.
- ryanmarsh 8y agoThis is the exact method I use to teach TDD and it works. It works really well. The article mentions the sunk costs fallacy. This is true. In the workshop I give you tougher and tougher parameters, shorter and shorter time frames. If your tests aren’t passing you reset to the last commit. You can’t commit without passing tests. People learn, a) write smaller simpler tests, b) code is cheap. The third time you write something you’ll write it faster and better. Better to do that in a tight loop than over a prolonged dev-test-refactor cycle that could last days to years. You also learn how to refactor in the green. Boy is that a good skill to have.
- microtherion 8y agoBut doesn't the classical TDD process start by writing a test and making sure it fails before the code is changed?
- sdenton4 8y agoGenerally if I make a change and nothing fails, I assume something has gone even more wrong than usual...
- andreygrehov 8y agoheh, i love it. Sounds like a military type of training, lol. I'll give it a try.
- deleted 8y ago[deleted]
- ridiculous_fish 8y agoHow do high-quality narrative comments get written in this workflow? Either I write the comment up-front and risk it being deleted, or I write the comments after the tests pass and thereby propagate uncommented code.
- inerte 8y agoCommit your comments. Your tests should pass.
- ridiculous_fish 8y agoThe idea of this workflow is that saving runs the tests, and then either commits or deletes all changes. I think in this workflow I would write the code, get the tests to pass (thereby creating a commit), and then create a new commit with good comments.
- inerte 8y agoSure. Or, write your comments. Run the script, tests pass, comments are committed.
- wink 8y agoThis sounds highly useful for changes you can do in a few hours. Or for toy projects where I can keep the whole application in my head. I have no idea how this should work on a big refactoring where you touch every single one of your.. say 200 files and add something to every single method. (Don't ask, assume a change that moves every single primary key to a composite key, so every getX(id) would become getX(id1, id2)). I mean, we were multiple people - we split out the work because a lot of it wasn't even very error prone, but this took days (ok, weeks). Are you supposed to just not commit or even show the stuff you did to the others? (Yes, there were many, many reviews and pair programming sessions in between). Of course this was on a feature branch. Yes, it lived too long, yes there were still bugs at the end when we fixed the tests. I don't think it would've gone better or quicker if we had meticulously broken down the exact same change (add an id to every getter and setter on controller and db-layer level). Actually we tried - and as expected it took deep dive through all layers.
- CzechTech 8y ago> This sounds highly useful for changes you can do in a few hours. Or for toy projects where I can keep the whole application in my head. Welcome to HN.
- bilalq 8y agoThis brings Vigil lang back to mind: https://github.com/munificent/vigil https://github.com/munificent/vigil It's a programming language that erases any code that fails to meet contracts or throws an exception.
- supermatt 8y agoThe problem with this is making changes TOO small - then you can find you are testing the wrong behaviour just to make your code committable. I think there is a balance to finding the right size increment while still keeping direction