The last necessary element for our minimum-viable task-management software is the ability to mark tasks complete in the Web interface. Happily, we've done versions of every part of this task already:
TaskRepository
exposes.To see the approach I took, you can check out the current commit in the veery/
repository.
These changes raise larger issues.
I'm using identical filtering logic in the "/
" and "/add
" urls: in each place, I want to display only outstanding tasks. (It's not very helpful there to see a list of every task you've ever completed.) There are a few possible reactions one could have to repeating logic like this:
get_all_outstanding_tasks()
should be another method TaskRepository
exposes.These questions of whether and how to deduplicate logic and information are some of the trickiest (or, at least, they score very high on a frequency-weighted measure of trickiness). You might have heard the slogan "DRY" ("Don't Repeat Yourself"). It's a worthy goal--as one goal among many--but is silent on the question of whether you're repeating yourself! To take a trivial example, if you have variables named my_hammer
and my_lammer
, nobody[1] would claim that DRY obligates you to "factor out" the ammer
, defining a preprocessor macro so that these become my_h{{HAMMER_LAMMER_SUFFIX}}
and my_l{{HAMMER_LAMMER_SUFFIX}}
.
That sort of "deduplication" is not only self-defeating; it is a response to something that wasn't duplication in any meaningful sense. And there are many non-joke examples of mere surface similarity to which factoring-out is an inappropriate response. There's no substitute for metaphysics here: you need to figure out whether the similarity is essential or accidental, and (relatedly) whether the instances are likely to evolve together or not. It's fine to make mistakes here--or, at least, you will make mistakes here, so it's best to be fine with it. If you're even thinking about these questions, you'll do much better than you would if you reflexively factor out every instance of surface similarity.
Another warning: call a (hypothetical) method like get_all_outstanding_tasks()
a "convenience method." Without presenting a full analysis of the tradeoffs one makes by defining such a method, note that the costs are a bit heavier when such a method is added to an interface. An interface is a sort of contract to which not only a given class is beholden. It becomes part of the contract that you've designed for multiple and future use. You're not just giving callers a convenience function; you're making that method a part of what that interface is, with all the cognitive and implementational burdens that come with that.
(You might be thinking that you can simply add the convenience method to an implementer of the interface, and not to the interface itself. That's much, much worse. If you do that, you aren't coding against the interface any more. You can't swap implementations in and out; it will make testing harder; you're breaking the barriers that make your software sturdy. Your interfaces are the dams keeping local difficulties from flooding adjacent parts of your system. Respect them.)
Anyway! I repeated logic in the two Web views because:
/
" URL a proper home page);There's much, much more to say about deduplication, code clarity, and interface design. Here are some resources. The point I most want to make here is a psychological one: reflexive factorization (i) is easier than really thinking through the question of whether some duplication is merely apparent and (ii) can feel "professional" or "responsible" in a way that not factoring doesn't. So it can, perhaps counterintuitively, require extra effort to leave apparent duplication in place.
Good luck!
[1] I mean, I'm sure it's been done. Let's say almost nobody.
Next post: Python task manager from scratch, part 27: Using a real database