Todo-or-die – Provides procedural macros that act as checked reminders
github.com
github.com
1. Want to run a build from a version from a few months ago, well things are fixed at the head of the main branch, but a todo in the old version are now bad and break the build. No binary searching for where a bug was introduced, or doing a patch of a previous release.
2. Github is down or you don't have internet access, now one of the key strengths of git (distributed, works offline) no longer works.
3. You have a large team, now nobody can get any work done for the length of time it takes a single person to fix the problem.
4. Your automatic cut for the weekly release runs at 3am, but a todo expired at midnight. Now the person in charge of the push to prod needs to do manual work, probably slowing down the cadence of testing the release.
5. Rust has bad compile times, this will only make it worse.
For 3-4 you can set TODO_OR_DIE_SKIP, as documented for the crate, to disable all the todo checks at compile time.
5 is getting better over time.
//! # Skipping checks
//!
//! If the environment variable `TODO_OR_DIE_SKIP` is set all macros will do nothing and
//! immediately succeed. This can for example be used to skip checks locally and only perform them
//! on CI.Maybe it should emit a warning when the issue is closed and then only error if it has been closed for a year.
Should probably be opt-in, enabled in CI.
Call it cargo todo-or-die or something.
It's almost like an assert for the environment. Maybe in the same vein they should be compiled-out for production builds? (I see in the docs they can be disabled with a flag.) But of course, if builds don't work, then people won't use it or will turn it off, like asserts.
In particular, this highlights just how much I don't think I would want to use this. Especially for environmental changes like a ticket got closed in some 3p project, or a dependency got updated. Probably the main case is the (hopefully rare) times you have to put in a dirty hack, that you _know_ will stop working at a certain date.
Otherwise, the response seems disproportionate. If it's time-intensive to fix and nothing was actually broken, failing the build is a recipe for getting bypassed. Squeaky wheel gets the grease.
Of course, proportionately motivating you to finish medium-ish term projects with questionable immediate value would solve tech debt in general :P
There's nothing that prevents you from commenting out the todo_or_die call that references a GitHub issue and replacing it with a todo_or_die call for next week.
Merge request with deleted todos without the fix would not be accepted.
Commenting out is a slight inconvenience, just enough to make sure it gets done.
Warnings are nice since we can choose how to handle them. Usually it's one of the following cases:
- A warning appears to tell us that some workaround is no longer needed, after we just bumped 'dependency-foo' to a newer version. We can delete that workaround as part of our version bump, reducing our codebase's complexity. (Deleting the workaround has a risk of causing unexpected problems, but so does the update of dependency-foo; in fact, the out-of-date workaround itself is a risk for the update, so deleting it is usually a good idea)
- A warning appears but we're rushing to hit a tight deadline. Things seem to work, so we can take a look later. (This is fine, as long as we actually do the maintenance later ;) )
- A warning appears to tell us that a dependency with known problems has been updated, so our workarounds might not be needed anymore. We try disabling the workaround, but the problem still exists in the new version; we update the check with the new version number, so the warning disappears for now but will re-appear next time that dependency changes.
- A warning appears, but we can ignore it since we're building an old version of our application (e.g. to investigate a regression).
// TODO (AS-1234): network errors should be handled correctly here
Where AS-1234 is the JIRA id. That way you keep track of them and they are part of your planning discussions. It's simple to do, can potentially be enforced by a linter, and help your product manager understand some of the technical debt.
Nothing breaks flow quickly than getting stopped by a linter, when an autofixer could be used behind the scenes without ping ponging the developer with linting errors.
That's a huge jump in complexity and possibly introducing security issues. Not only does the CI now need to lint your code, it also has to have credentials to your planning tool, and logic for creating issues there when specific things end up in the code. All because a developer couldn't be bothered to add a ticket in the planning tool?
> Nothing breaks flow quickly than getting stopped by a linter, when an autofixer could be used behind the scenes without ping ponging the developer with linting errors.
Linting errors should show up right before commits, and (by default) prevent developers from committing if the code doesn't work (by the standards of linting, checks and tests). This is no different from static type checking then.
If you're waiting for CI runs to see what's wrong with your code, then your feedback cycle is already too slow.
Possibly, I have been blessed in not having to directly deal with Jira's complexity in years. On the other hand if it has support for no read, post only ticket type permission for API tokens I see no issue.
> Linting errors should show up right before commits
That's an opinion I do not share. Don't waste my time with linters that can autofix things for you. A linter is different than a type checker. Sure some type checkers are implemented as linters, but a bunch of linters are style preference enforcers.
In short my opinion is: if your linter has a fix option, drop that in your PR CI and have it autocommit.
There are several differences between a TODO in comments and a compile time check:
1. Such a linter would work for all possible languages with minimal changes - ie. adding the pattern for a comment.
2. You can choose to run the linter only for modified lines.
3. If you need, you can run and gather all TODOs in a project or across the whole codebase from time to time.
4. You can change the requirements - ie. adding an issue/task or the owner to the text of the TODO - without having to update all the old TODOs
5. It doesn't impact the compile time, since it is a comment
Though there are still risks it might suddenly hinder you from releasing a build in an emergency.
I get what this is trying to do, but there's got to be a better mechanism to let your developers know that e.g. a new feature exists upstream than suddenly breaking the build.
The very first example is of this:
// trigger a compile error if we're past a certain date
todo_or_die::after_date!(3000, 1, 1); // its the year 3000!
And the second and third examples (this is all of them) are explicitly relying on non-versioned, external state leaking in, too. // or a GitHub issue has closed
todo_or_die::issue_closed!("rust-lang", "rust", 44265); // GATs are here!
// or the latest version of a crate matches some expression
todo_or_die::crates_io!("serde", ">1.0.9000"); // its over 9000!Take the time example: if your build has access to the actual clock time, your build is unreproducible, period. For reproducibile build you need a constant, injected clock time.
So I'd rather day that if you have reproducibile builds this macros won't work at all.
But it’s a one-way implication. If your build DOES depend on the time, it can be made reproducible by setting it to a constant fixed value.
Although I'd prefer having this throw warnings instead of errors at least locally.
(TODO:by-version (0 1 1) "Fix crash on leap seconds")
(TODO:by-version (1 0 0) "Implent a sane CLI")
implemented with using a macro. That was actually quite useful for all those small projects I didn't code on weekly, and scaled well for the codebases I worked on (mostly alone), which were all less than 5k loc.Edit: it was perhaps implicit in my comment. Normally we/I won't ship/release if unit tests fail during CI.
They could even be tests if your own base layer, depending on how your application is structured.
It's pretty frustrating, to be honest, but that's more of an indicator of leaving stuff by the wayside. But you will definitely get negative vibes for introducing this strategy into a codebase, even if it's unwarranted.
@DisabledUntil(...)
to give the team some time where failures are ignored to de-flake the tests but keep stuff moving.
How the rust macro communicate to Github APIs in order to check issue status?
The biggest issue was, the magic we'd come up with to prevent spurious closing/opening of these issues was insufficient for formatting and reorganization, and it resulted in unnecessary churn in the issue tracker and for some comments on these issues to be lost. But done better I think it could still be valuable.
There are 2 trends here:
1. Everything-as-a-code stored in the git repo in a text format close to the actual source code. If you store documention-as-a-code, diagrams-as-a-code, IaaC, tests-as-a-code, loadtesting-as-a-code, etc. - then why not issues/tickets as a code also?
2. Reduce number of tools/services/mechanisms you use in order to eliminate impedance mismatch, simplify workflows, and remove barriers to collaboration. For example if you standardize on Github, then you can use Guthub Issues, Projects, Packages, Actions, Codespaces, etc. And most developers are already having Github a/c.
Postmodernism gets a bad rap from modernists as being anti-Truth, and you can see this somewhat in how a Subversion diehard might view Git, “okay but what is the real truth of the code?” Git had this from kernel development, the real truth is that there are about a million different Linuxes out there due to combinatorial explosion, this one is kernel 4.15 with patches X, and Y applied, that one is 4.15 with X and Z applied...” It's not a philosophical choice but a practical reality for Linux. And so too, branch names in Git generally have process associated with them, they have to get their meaning by sociopolitical agreement among the developers and their tooling.
This conspicuous reference to tooling highlights something really important. Git’s postmodernism is constantly clashing with the modernism of Prod. We expect our websites to have one canonical authoritative Truth, at least until we start really committing to postmodernism making A/B testing etc. the norm.
You see this last point particularly in the context of Gitlab and GitHub doing CI/CD tasks.² And indeed now the general wisdom is to do trunk-based development, in other words try to ditch postmodernism at this level, thinking that way is too hard when we've got such a modernist reality in front of us, let us instead bound our postmodernism in modernism-plus-feature-toggles or or so.
Anyway, the reason I'm ranting about all of this to you is that the same modernism/postmodernism tension is present for issue tracking. Yes, logically an issue should be closed by a commit that fixes the issue. This means that an issue can be closed in production and still open in some feature branch, say. Makes perfect sense. Especially this plays well with one piece of advice I am always giving people, which is that the size of your repository, monorepo vs mini-repo, should be at the level that it makes sense to time-travel on.³
Except: we might like a very modernist view on our issues. We might want them to have a single known status, some person worked on it and finished it and now it is done. We want this issue to be in a Kanban board, say. Does a new issue get merged into all of the release candidates? Those sorts of questions. So again, trunk based development may be necessary to mitigate the mismatch.
[1] Postmodernism means a lot of different things to a lot of people. For a quick perspective to help this comment make sense, I take postmodernism as saying that there are blind-sages-and-the-elephant problems for any sufficiently interesting topic: there are multiple perspectives which each give you a partial truth, and only by seeing something from many perspectives do you get a well-rounded education on it. Furthermore, if people are saying “they're actually is only one correct perspective, one sage is Correct above all others, and it is This One” then that is best interpreted not as a genuine criticism of other ideas but as a power grab of sorts—the problem is sociopolitical and not technical.
[2] How do you configure those tasks? “With a file in the repository.” Oh, you mean a CI/CD branch that has one particular name and sets the rules for all branches, per modernist requirement? “No, like in the root of each branch’s folder structure. Like the prod and develop branches will both say “if my branch name is ‘prod’ then deploy me to production,” but this will only trigger for the one branch.” And if I push up a new branch called “hotfix” that says “deploy me to production if my name is ‘prod’ or ‘hotfix’?” Um, er, ah. This is a hard problem that both companies had to invent some sort of external-to-repo tooling for, because they both failed to understand the most basic thing about Git! You want a different take, look to Gerrit. ACLs are in the refs/meta/config branch—ah!
[3] That is, if you have microservices and it makes no sense to roll back one microservice 30 days without rolling back another, which will probably be using some new functionality that was added to the first—then you should version both together even if you build/release separately. (Git lets you get subtree hashes for this pretty easily.)
There is a certain strangeness that we have a very distributed and local notion of code, but _running_ that code (even in local environments) is much harder.
https://reproducible-builds.org/ https://bootstrappable.org/
Everything on PyPI is just random artifacts uploaded by whoever ran a build script on their machine - it's nuts.
This means that to let, e.g., testers add information to a ticket they have to do a code commit? I doubt they want to deal with that, and I don't particularly want to extend commit access to my repo to everyone who might need to interact with an issue.
Fossil SCM has all of the ticketing/bugs stored in the source control repo. Of course, in this case the repo is a SQLite database.
Also, reminds me of the blockchain oracle systems and smart contracts.
Cool idea, neat project!