The Code Is the To-Do List
executeprogram.comOne of my favorite small linters I made on a previous team:
// todo 2022-06-09 something
^ on that date, it'd fail the linter, and print the comment. Every TODO needed a date.Got a failure and need to get past it for now? No problem: bump the date for a week or something. Now at least two people are aware that it exists (author+reviewer)... and one is in the git history for that line. Makes it rather easy to trace back who wrote a TODO / who put it off / etc. Though we fairly quickly started adding usernames to the comments for who-to-contact.
Sometimes they're just stuff to do "soon", sometimes they're no longer necessary, there are lot of reasons to delay or delete todos. But oh boy did it work. We resolved or removed about 90% of them in 2 months, and the remaining ones quickly got tasks attached and had bigger plans built around them.
> Got a failure and need to get past it for now? No problem: bump the date for a week or something. Now at least two people are aware that it exists (author+reviewer)... and one is in the git history for that line.
That sounds like an awful strategy, one that needlessly creates problems and revision history noise and team distractions.
Is it awful if a comment leads your CICD pipeline to break? Now imagine having your CICD pipeline break because of a TODO item.
Just create a ticket in your ticket queue and get rid of that TODO.
I do that, create the ticket.
I want to believe it means one day we will do that TODO, it'll beat the priority of all the other tickets in the queue.
But it never does, there's always some critical new feature sales wants, or something bigger on fire.
I still create the tickets, as a kind of cathartic process, a confession to the jira gods, I have sinned and half assed something. Please take this ticket as my penance.
> But it never does, there's always some critical new feature sales wants, or something bigger on fire.
Then clearly it is not deserving of your attention according to the powers that be. If it is, then talk in the language that these parties understand better/prefer: bump up the priority of the task, add a "blocked by" or "has to be done after" link to these other issues in the tracker and tell the rest of the teammates that you'll be working on that piece of technical debt instead of the new feature.
If you can't do that, then either the "TODO" comments/issues aren't important enough, or they're not deemed to be and will/should simply be left to rot until you either have an abundance of time to address them (which may be never), or the project is retired.
If more pushback is necessary, then do it doing any estimates (provided that you have any): "Task $FOO will take X% more time due to $BAR not being finished and slowing down development. Consider doing $BAR first and $FOO should become less complex then."
That's definitely the way management wants you to work, and it's worth trying to see how it works out. But in many cases it's not in your personal best interest.
In those cases, there's a way to keep management happy and yourself too:
1) Realize that when management asks for time estimates, they don't want your median estimate, because you'll be late half the time and that messes up their scheduling. Give them an estimate that you'll meet at least 90% of the time.
2) This means in almost half the cases, you'll have extra time. You can give some of that back to management, but use some of the time to fix technical debt. The more debt, the more of the time you spend fixing it.
3) With less technical debt, you can speed up your estimates and still be at that 90% level. Now you're delivering as much as if you let management fill your time in the first place. Your code magically has fewer bugs, there aren't many unexpected delays, and you almost always meet your deadlines. But you still have plenty of free time to improve things even more, develop your skills, etc.
As a bonus, you have more of a sense of agency, which is an important factor in feeling happy at work.
> If more pushback is necessary, then do it doing any estimates (provided that you have any): "Task $FOO will take X% more time due to $BAR not being finished and slowing down development. Consider doing $BAR first and $FOO should become less complex then."
Creating those estimates alone is more than the sum of total work usually.
Then the problem of convincing others is itself typically more than the sum of total work.
Usually metrics aren't accurate enough to be able to prove these things anyway in my experience.
yeah, it takes more time in jira to create a task with a deadline and get it in a sprint and make sure it has an owner and find it in the backlog and move it through the workflow's steps than "is unused? delete". heck, sometimes the task takes less time than loading jira's website.
task systems are frequently awful at team-owned (not necessarily an individual) lightweight checks.
> Creating those estimates alone is more than the sum of total work usually.
If that was true then it would be trivial to just push a pull request and update the ticket to review it.
> Then the problem of convincing others is itself typically more than the sum of total work.
When that happens, that's the universe trying to explain to you that you're wasting your time with stuff that does not add tangible value.
This does not change with petty office politics moves such as ignoring your team's best judgement and continue pushing for something that was already decided to be a waste of time.
> When that happens, that's the universe trying to explain to you that you're wasting your time with stuff that does not add tangible value.
That's not always true, especially if it's tangible value that takes more than a week or two to see.
Tech is very biased to short-term rewards that cost more in the long term.
> This does not change with petty office politics moves such as ignoring your team's best judgement and continue pushing for something that was already decided to be a waste of time.
If this happens, it's usually because that team is conflict avoidant or only minimally addresses surface level concerns in disagreements.
If you just want to get your paycheck, that works fine. If you are interested in improving, this is frustrating because the correct or more correct answers are never worth the time.
> Creating those estimates alone is more than the sum of total work usually.
Then simply add those hours spent estimating things to whatever time management solution that you use personally (for insights into where your time goes each quarter/year) or that your company mandates (for basically the same thing). If it's an actual problem, then simply raise it as such later down the road and look for ways to streamline things.
Most of the time you shouldn't care about how much time something takes (exceptions exist, based on the industry/project, but many deadlines are made up anyways), merely that the time to be spent on the task has been approved and that you're not doing something that isn't documented/categorized, e.g. time that "disappears" by being spent on things that don't have any sort of visibility/basis for taking up your time.
If changing how a button works takes 2 hours but writing tests takes 4 and refactoring old code takes another hour, then let the record show exactly that. If unclear requirements cause you to spend another hour or two in meetings and talking to people who didn't document code or explain their changes in merge/pull requests, then let the record show that, too.
Of course, this might vary on a per-company/project/culture basis.
> Then the problem of convincing others is itself typically more than the sum of total work.
If others in the team don't want to fix these things, then they probably aren't as important/annoying as you think they are and therefore probably won't be prioritized so highly anyways. When there are really annoying/brittle API integrations, for example, most devs usually should back you up in your efforts in making things less horrible.
> Usually metrics aren't accurate enough to be able to prove these things anyway in my experience.
You don't actually need metrics that are completely accurate, because finding ones that are is seldom easy to do or even possible, or simply not worth the effort. Having something to the tune of "This API integration can be improved, the last 3 feature requests related to it exceeded their estimates by quite a bit" should be enough, provided that you can get the other people on board.
If you cannot, then it's probably an issue of the actual work environment/culture and/or soft skills.
>Then simply add those hours spent estimating things to whatever time management solution that you use personally (for insights into where your time goes each quarter/year) or that your company mandates (for basically the same thing).
Which sometimes means it doesn't happen because it's not valuable enough at any given instant to go through that multi-hour process, yes.
Lightweight tasks exist. As much as I generally agree with you, following your rules means you either do not ever do them, or you bloat them to multiple times larger than necessary. And correcting a company's task-management process as a whole is not always feasible either.
> Lightweight tasks exist. As much as I generally agree with you, following your rules means you either do not ever do them, or you bloat them to multiple times larger than necessary. And correcting a company's task-management process as a whole is not always feasible either.
Creating a short Jira issue and poking someone about including it in the current sprint should take about 10 minutes in total, if you want a decent description and are okay with occasionally not focusing on the "ceremonies" of sprint grooming too much, since as you say, some tasks are indeed small (and as long as this doesn't lead to scope creep, e.g. fixes/refactoring instead of new features this way).
Of course, that may always not be viable and i see where you're coming from - yours is also a valid stance to take and i see why focusing on an issue tracker too much would be cumbersome. Then again, in my eyes that's a bit like the difference between having almost entirely empty pull/merge requests and having a description of why the changes exist, a summary of what's done (high level overview), additional relevant details and images/GIFs of the changes in action, DB schema diagrams or anything of the sort.
I feel like additional information will always be useful, as long as it doesn't get in the way of getting things done (for an analogy, think along the lines of covering most of the business logic with tests vs doing TDD against a bloated web framework and thus not getting anything done - a few tradeoffs to be made).
> I want to believe it means one day we will do that TODO, it'll beat the priority of all the other tickets in the queue.
> But it never does, there's always some critical new feature sales wants, or something bigger on fire.
Why do you expect that random comments in the code will affect the priority of some tasks? Do you feel that stashing out-of-band info on pending tasks which were deemed not important regarding the project workload changes anything?
Also, if everything is always more important than the TODO item, that is the universe telling you that your TODO item should be deleted and that you should stop wasting your bandwidth with useless and unnecessary tasks.
That TODO item is just the receipt for a few bytes of technical debt you took with the universe. A few bytes is nothing compared to Management's roadmap, so it gets ignored. But have no doubt: the universe always returns to claim its technical debt...
> That TODO item is just the receipt for a few bytes of technical debt you took with the universe.
It really isn't.
A TODO item that you feel does not justify a ticket is just a subjective nice-to-have expressed as noise/a declaration of intent with no intention to deliver, which ultimately only results in noise.
It's not even technical debt. At most, it's a pledge to goldplate something without being able to argue any tangible upside.
> A few bytes is nothing compared to Management's roadmap, so it gets ignored.
Bytes are irrelevant. Tickets also cost bytes. Tickets also track rationale and context. What really matters is allocated resources in order to deliver value.
The only reason your TODO item gets ignored is because the potential value it promises does not justify allocating resources to it.
People need to be smart about how they invest their time and effort. Tracking vague tasks deemed unnecessary or useless in a separate out-of-band source of info is not productive and ends up only creating noise and distractions.
> At most, it's a pledge to goldplate something without being able to argue any tangible upside.
One person's gold plating with no tangible upside is another person's not be woken up by pagerduty at 3am ;)
In Eastern Liturgy, the ticket is the confession and the mandated overtime is the penance.
You could have the best of both worlds by making the CI job search the backlog for overdue critical tasks.
I don't get why so many people likes this. No, I don't want a suddenly failing pipeline on master because of an expiring TODO.
A better approach is for a TODO to be required to reference a ticket, and add a ticket. Prioritization should happen in your issue tracking, not in the code.
Many TODOs (the majority in code bases I've worked on that had them) do not require any action, they are annotations to the code base about future potential development or research. Addressing the TODO, either by removal or implementation, could be deferred indefinitely with no consequences and often are. These aren't things that need to be fixed per se. For example, I've seen TODOs widely used to document optimization or generalization opportunities in the code base.
You do not want to pollute your ticketing system with myriad TODOs. They are not meaningfully actionable and they tend to require very local code context so it is easier to understand them as code annotations.
Those can just be added without "TODO" then? You're explicitly saying that you're marking things you are not likely to do as to-do, that seems like a bad idea whatever your preference wrt automation is.
The point is to concisely distinguish proposed action by maintainers, at whatever level of urgency, from purely descriptive comments intended for the benefit of users.
Because the annoyance means it will get fixed? In issue tracking it just gets archived.
> Because the annoyance means it will get fixed? In issue tracking it just gets archived.
That makes no sense at all.
If a ticket is archived them that means the issue was evaluated and considered irrelevant and a distraction that should be ignored. Otherwise in the very least it should be placed on a backlog, and reconsidered each and every single time someone picks/is assigned a new task.
Nagging through out-of-band means to push an irrelevant task that was already considered and discarded makes all matters worse, and frankly sounds like a playground for petty passive-aggressive office politics.
If you want to set it a date, then set a deadline for the issue. It should still annoy the right person.
My team also dates its todo items by the date of creation.
I have played with the idea of failing building or linting after a specific date, but I have said no. I want to be able to check out a specific commit and have the same experience as when the commit was created. Having the linter fail based on the current computer clock setting would make that impossible.
Definitely agreed, don't tie linting like this to your build system. It'll also suck if it blocks fixing an urgent problem.
I had a similar idea a few years ago, but with a git hook.
Yeah, a lightly-annoying-when-passed hook would probably be a better fit for smaller environments / personal use. I may have to set that up for my own stuff, thanks!
I set mine up in our CI environment because it was essentially trivial to do so, and it also caught people who didn't have hooks installed... but that's only really relevant because we had a couple dozen engineers rotating between many other projects that religiously avoid hooks. It was far too likely to be forgotten unless it was automated.
I'm going to give this a try. Thanks for throwing the idea out there!
We did eventually add a way to skip it outright per commit fwiw, to avoid merge conflicts when there were a lot of changes flying simultaneously. That may be worth planning on to some degree :) or show a warning if it's within a week or something.
I'll also recommend generally treating it lightheartedly - the person who it interrupts is not usually the person best suited for tackling it. A no-questions-asked "you can always push the date off a bit" policy helped - poke the author of the cryptic comment so they know, and that's the end of the immediate responsibility. Target just enough friction and shared face-palming at forgetting so you don't ignore it outright, like often happens with merely printed lints, but if it becomes annoying it'll be avoided and then it's just yet another Jira competitor.
> Every TODO needed a date
A todo is a todo. I don't see why a date is necessary.
It was just to ensure it wasn't forgotten. If it's "long term", stick it for a year out or something, it's fine. Truly timeless? Year 2999 works pretty well, or just don't use "todo" because it apparently isn't gonna happen.
We fairly often used it for stuff like "X should be upgraded to fix Y by now, see if this hack is still necessary". If was not, just push it out and check again then. Or do whatever else might be necessary since it didn't meet expectations.
I've encountered many codebases with todos that are years old, sometimes authored by people no longer at the company. A date ensures these aren't lost forever. You can always just push it another year out.
If your TODO is forgotten, it's overwhelming likely that it wasn't very important.
You annotate the code to save some context for the next time you read it. A TODO is basically an anchor so you know where to come back as soon as you finish that requisite you are doing now. If you get anxious about deleting them, you may need to rethink your process, because it's not a suitable place for the kind of information you get anxious about losing.
>If your TODO is forgotten, it's overwhelming likely that it wasn't very important.
Sometimes, but not always. We deleted the ones that were not, and finished the ones that were.
As far as noticing these by hand: my current project is about 600,000 lines of hand-written code, not including several libraries we also author and use in it, nor other projects I'm also heavily involved in. I haven't even looked at all the code yet, much less do so on any regular cadence. Forgetting is something you plan on and address up-front, not something you just let happen.
> It was just to ensure it wasn't forgotten.
Why don't you just add a ticket in a ticket backlog, then?
Most of the time it's not possible to trust items you add to the backlog related to tech debt will actually get done.
Why make lists that will never be completed?
Or put another way, there is no pain that can't be avoided by ignoring the backlog.
You can only ignore these TODOs until your build fails, then you have to reflect on whether to do them, push them into the future, or delete them.
I think this works because it really makes backlog grooming a group activity where everyone is involved, so everyone feels they have a say in setting priority.
You can grep for todo in your codebase. It will list them.
> A todo is a todo. I don't see why a date is necessary.
Indeed a date in a TODO is irrelevant, and more importantly revision control systems already track the date when the TODO was added and updated.
It would be a cool little side project to automatically track TODO's and FIXMEs with an issue tracker. There is a lot of monkey work involved with issue tracking currently and a lot of work that isn't tracked or poorly tracked because of it. Lowering the barrier to make work and tasks more visible is useful.
Jira creates so much friction. It's a trillion dollar drag on the Tech industry. It should be as quick as writing a Slack message.
How do I get such a linter into Visual Studio, I've never added anything like that to C#, my personal strategy is to just make tickets, but also with stuff like JetBrains different IDEs you can fetch all the TODO's in a list. I opt for tickets so they dont get lost.
VS automatically creates a To Do list window with all your TODO and similar comments. If you don't want to go as far as making it a git hook or built into your CI, you can use something like this: https://marketplace.visualstudio.com/items?itemName=MattLace...
Even better: do it now, or not at all.
If it is a failing test do it now anyway.
The reason is the person adding the TODO may have a bias that their thing really is the most important tech debt.
However I would step back and figure out what tech debt payback offers the best ROI with the team.
If you have a pet hate refactor you want done, add it to a personal list and bring it up for consideration in that process.
Or just fix it as part of your task if practical to do so.
isn't tracking date and username provided by git (blame)?
The authoring date is, yes. But not the "remind me at" date.
As for author: yes definitely. And it's a great fallback option. But it's not visible in most code review UIs, and even when it is it tends to be the most recent editor, not the author. Git's CLI makes the full history of a line pretty easy to read with -L (usually, depending on if it detects a move or not), but have you ever tried to do that on GitHub? Back through several versions? It's a freaking nightmare, and nobody will try to do that while reading a diff.
nice trick, placing scheduling in code seems interesting
Along these lines, I find a "nocommit" git hook [1] to be an absolute life saver. If for whatever reason I need to temporarily break a piece of code, I just add a `// NOCOMMIT` comment above it, and git won't let me commit if I forget to change it back.
I had a different approach but only for personal projects. I used Emacs org-capture to do this as a ToDo. It would remember the file, line and I could schedule a date for it. I used to plan my days using org-agenda and the TODO would show up as a task for the date on which I scheduled it. It would also gather information from emails and other things so on the overall, it was a workable system. No syncing to the outside world and stuff like that though. Also, not very usable in a collaborative environment. Worked very well for personal projects though.
I use FIXME comments for this, though my team's project doesn't have esLint set up to error on them, and I don't think the rest of the team would be good with modifying the esLint settings we've used for so long. But I have the TODO Tree extension for VSCode, so I can quickly jump to any FIXME comments when I need to go back to do stuff, and I don't open any PRs until there are no FIXME's left, so it's a self-enforced version of what the blog mentions.
Unless I've misunderstood, I don't like this proposal. To me:
Scenario 1: The code needs doing as part of the work I am doing. Solution: Do it before your current branch is merged otherwise your story is not complete.
Scenario 2: The code will eventually need doing but not now. Raise a task/ticket. Don't pollute the code with things that might never get done. We intended to remove a load of old code previously and haven't done it in 2 years because it isn't a problem.
I don't understand why you want to carry on working but hold back the entire codebase until the second piece of work is done.
As I understand it, this is a forced reminder for scenario 1; to ensure the other change is also made before merge.
One trick I employ for myself is:
throw "TODO" // <comment with some additional context to remember>
Substitute for your language's syntax of course.I find this especially handy when writing test specs, but it's also handy when creating new functions etc. For tests I lay out the test descriptions with just this one line in the function body. For new functions/methods I can define the function and its return type (assuming a type-safe language) which makes the code using it compile, but leaves the implementation TBD.
The placeholders are then implicitly flagged in test runs and easily found. Basically my text search results for 'throw "TODO"' become my TODO list.
That sounds good within a feature branch but you wouldn’t merge those to main, right?
No definitely not, this is just for WIP branches. I usually don’t even push these, unless it’s something big that I need early review on.
to be fair, that is what the linked post is talking about :) so super-explicitly breaking things serves pretty much the same purpose, as long as you have attentive reviewers and/or tests that touch that code.
Mark Seeman wrote a book about Writing code that fits in your head.
It's a really good read.
https://blog.ploeh.dk/2021/06/14/new-book-code-that-fits-in-...
In code review, I ask that any TODO be augmented with a link to the jira ticket tracking it. If it is not in jira, it is not getting priority. If it is too small to justify a ticket, fix it then and there.
I dislike //TODOs for the same reason I dislike commented out code.
It adds noise to the code with no actual benefit. If a TODO is outdated it actually becomes a false flag, it's like a comment that doesn't reflect the code changes so now it adds to the confusion instead of alleviate it.
Personally I add // FIXME while writing code but I make sure to remove all of them prior to creating a merge request. It's my way to track outlier issues that I will turn around and fix as I parse my code. If I find a // FIXME that deserves to be its own enhancement or a future TODO I will capture it as a story. My org is Agile story driven which is why I put it there.
Perhaps adding TODOs for a personal project is fine. However for repositories that multiple developers touch a TODO at best is noise, at worst a point of confusion.
I have another method where I simply create a commit with nothing but the TODO/FIXME/DELETEME comments where the commit message is same content. I then make sure to delete all of those commits later before creating a pull request. If I forget any of it, it's obvious just by looking at the list of commits.
This fits my workflow because
1) Github and pull requests
2) our normal commit messages start with an emoji (:+1: or :wrench:)
3) there's rarely more than 10 commits per PR so you can't miss it
I've had great success with a lot of smaller engineering teams (3-8 Engineers) just using a simple grep of codebase to write out TODOs to a seperate file at root directory of the and make this part of the build process. eg
task updateTODO(type: Exec) {
commandLine "bash", "-c", "grep -ri \"\\bTODO\\b\" src > TODO"
}
Advantages:- It's unintrusive and the TODOs get updated automatically (Also doesn't create sudden build failures like some approaches suggested on this thread)
- It's in the git repo so accessible to everyone easily and you don't need any special tools/plugins to see TODOs. Even better, I can just give a link (since it's just a github link) to the TODOs file to someone like a PM/PO to get time for my team for engineering tasks without needing to go through creating tickets and "BaCkLoG GrOOmINg" stuff.
- Every few months, we organize a refactoring sprint where having all TODOs in a single place allows engineering team to sit together and see what the TODOs are that need to be handled.
I just put //TODO:<my initials> and don't see a linter for it adding anything really. I don't think I've ever checked in that way, because I read all my code right before I check in, but maybe this is a difference in workflow (I've mostly used p4; I can see a git workflow that involves lots of local commits being different.)
I used to create a lot of TODOs in the code to track things but some developers just do not prefer this way.
My team later switches to more formal project management tools to keep track of TODOs and other tasks (e.g. follow up with clients, etc) and this seems to work better for us.
> I used to create a lot of TODOs in the code to track things but some developers just do not prefer this way.
Requiring TODO items to be created and tracked in an issue tracking system instead of sprinkling comments in source code is a no-brainer, quite honestly.
Issue/bug tracking systems were explicitly created to help teams track pending work. It makes no sense at all to misuse code comments to do the exact same job that's already done by any ticketing system.
Complaining that TODO/FIXIT comments are forgotten when any team workflow is based on constantly reviewing pending tickets and assigning them to team members is something that's hard, if not outright impossible, to justify.
My stack has sth like that on by default. It uses the common words: TODO, FIXME, BUG.
I can’t merge a pull request, if there is one new todo added.
And also my IDE (jetbrains) shows me a warning, when I want to commit/push code with new todos. And it has a tool window that lists them.
Nice article and idea. I've used TBD comments for years but it was always up to me to informally circle back later and deal with them. An automated commit/merge-stopper seems like a good way to approach it.
The observations about maintaining the context of what you're working on in your short-term memory, and how much faster you can do things that require it while you have it are very true.
This is a great idea. But I would like my eslint to produce a WARNING, not an ERROR when I have a certain keyword in a comment. Like if I wrote FIXME in a comment I would like it to produce and eslint warning which would let the code compile and reload (in create-react-app let's say) but it wouldn't build in CI because it has a warning. Is there a way to do this with eslint?
This is exactly what the article describes. Lint ERRORs that block CI but not compilation.
No unfortunately for me using create-react-app an ERROR like this article's config produces blocks compilation as well. I would need it to output a WARNING for compilation to continue but I can't see how to do that with no-warning-comments
How could such a linter rule be enforced with python? does such a linter rule exist here, too?
This technique is pretty trivial, and thus very generalizable.
When I was at Google as a new grad, I used to put comments with the wrong number of slashes for my internal todos, which would cause the regular linter to pick it up before review. You can piggyback on any such linter rule very easily.
My usual was DO NOT SUBMIT which would show up in all red in the editors and code review tools after the woodly-doodly incident (someone changed WD to woodly doodly to test they were seeing their code and accidentally shipped it). It’s easy to teach your tools about a magic phrase like this
I always use code comments with my name during the work to help me remember things I want to change but don't want to do them right in this moment. Then before pushing I make sure there is no comment bearing my name.
I use // FIXME for the same reason. When I get ready to create my merge request a global // FIXME will point out anything I missed.
May be combine this idea with Knuth’s Literate Programming? Then when you get around actually working on the task it already tells you what you’re supposed to do!
I like todo comments, but I feel like they miss a dashboard.
Could have been a cool approach of GitHub or gitlabs issue tracker to be some sort of to-do comment tracker
I write a whole bunch of //hhh and //hhh1 (and so on) comments for myself and have a pre-commit hook to scan for them.
The beauty of this idea is that it solves the usual to-do list problem of adding a task and then ignoring it forever.
I'm pretty sure "XXX" in their case was chosen exactly because of the more general meaning. I'd sit on a bunch of their code reviews meetings, I bet they have a lot of fun when one of those TODO's pop-up.