13 ms·
Shitlist Driven Development (2016)
- baxtr 6y agoIn case you wonder what shitlist means: > “shitlists”: a whitelist of deprecated behaviour. Existing deprecated behaviour is OK and whitelisted.
- WJW 6y agoThis is an excellent way of using your (hopefully) already existing testing and CI infrastructure to change a social problem into a technical problem. As we all know, technical problems are by far the easier of the two. :) We used this technique to great effect at my previous day job, though under the more neutral name of "ratchet".
- trevyn 6y agoI like the term “ratchet” more because it communicates the underlying mechanism, which may be a useful tool in a broader set of applications.
- Jumziey 6y agoBrilliant. It's a really good compliment to strangling certain parts of a large code base. As always the key is getting the information to the right people at the right time, and making it more difficult to make the wrong choices then the right choices.
- notabul 6y agoThe idea of deprecation and death of code assumes that those using it can easily stop. That’s not always the case. When Flash dies Jan 12, 2021, you’ll see what I mean. There’s been so much misinformation about it, but there’s an OS datetime check in the Flash viewer code, browsers will disable the plugin even for some older versions not just new releases and have or will remove PPAPI and NPAPI support, and Windows already has an optional update to kill Flash support that will be part of regular updates in Summer 2021. Even with three years of warning, it’s not ok. It’s an extreme case of deprecation, being attempted by the best in the business, but it is inherently bad, and there’s hardly a good way of doing it.
- JBorrow 6y agoI don't understand what this has to do with the article or the previous comment at all.
- notabul 6y agoThe article is about deprecating and taking functionality out of use. My point is that deprecation is by its very nature going to hurt something that isn’t prepared for its demise. There is no good way to do it. There are only less bad ways to do it.
- V-2 6y agoFlash is an example of an entire technology getting deprecated. The article only talks about the codebase level.
- adewinter 6y agoAt this point if you/your company is still using Flash I find it hard to sympathize with you. How did the company come to the conclusion that using Flash was a good business decision? It's been largely unsupported/disabled in mainstream browsers for _years_ now. They also knew for 3 years it was officially going to be deprecated in 2021. Sounds like despite that they continued to develop on the technology anyway?
- deleted 6y ago[deleted]
- notabul 6y agoSome businesses run on code more than ten years old. They don’t care if you sympathize. The business or institution decides how it wants to prioritize replacement, and some projects miss deadlines. If you think that every application running in an enterprise has stopped using Flash, you’re sorely mistaken.
- 6y ago
- harryf 6y agoExactly. What's nice about this approach is it factors in the "behavioural economics" of developers. As the author puts it... > At the end of the day, everyone needs to get work done, and if they see a code-path already being used from 10 places in the code-base despite these soft warnings–it doesn’t seem crazy to introduce another. Even the best person with the best intentions will take a short cut under some circumstances, meaning to fix it later then getting distracted before that happens.
- blargmaster42_8 6y agowhitelist triggered
- pbronez 6y agoHere, a “shitlist” tracks existing uses of deprecated behavior within a large code base. It prevents new uses of deprecated behavior without disrupting legacy code. This is a temporary measure used to facilitate refactoring large code bases.
- tantalor 6y agoNot necessarily temporary. Abandoned (but critical) code might stay on the shitlist forever if it's not worth migrating it.
- Uberphallus 6y agoShitlist driven migration/refactor, maybe. If it's development it means you're always deprecating something and that's a significant organizational smell.
- hinkley 6y agoIf software is organic, and there are limits to growth, how is it that you don’t always have something changing? And why is it an organizational smell?
- Uberphallus 6y agoMaybe we have different views of what "changing" means? Every software is different, so take my words with a grain of salt, but personally, if it's always changing its behavior/interfaces/architecture (as opposed to just growing or a healthy mix of both) to me that sounds like either: - It's software that runs with a highly volatile and niche functional target (scrapers, certain bots and business/operation research come to mind). - It's a snowball of technical debt asking for a rewrite that was never given a green light. Sometimes an evolution is needed and then shitlists have a place. If you guide development through shitlists it means you always have shit to get rid of. Maybe you have a ball of shit, in that case?
- Tade0 6y agoI kept a personal shitlist in one project but it was rather a collection of "worst practices" - things I vowed never to do. It was personal because the idea didn't get a lot of attention when I brought it up.
- codeulike 6y ago'Shitelist' would rhyme better with 'Whitelist'
- deleted 6y ago[deleted]
- molsson 6y agoWe used a similar approach when we pushed to get -Werror enabled in a large C++ code base I worked on years ago.
- deleted 6y ago[deleted]
- kenerwin88 6y agoSuch a great idea, thank you for sharing!
- Remaged 6y agoFor the Java Developers here, have a look at archunit. We use this to discourage certain behaviour but you can also use it to test and whitelist the usage of shitlist items => https://www.archunit.org/ https://www.archunit.org/
- mellosouls 6y agoTo an extent this seems to be related to more general automated detection of refactor targets/"code smells" (ironically) for which there exist tools already (e.g. linters indicated in the article) - I like this simple "list" approach though.
- quickthrower2 6y agoGot this T-shirt! It’s useful in regular sized code bases too. We used it for certain kinds of linting and code style errors.
- dhd415 6y agoA similar kind of automated mechanism is required in distributed systems that allow for rolling upgrades. New functionality in upgraded nodes can't break not-yet-upgraded nodes and legacy behavior in not-yet-upgraded nodes has to be tolerated in upgraded nodes but only until the entire system is upgraded and then it is prohibited. Doing this wrong results in some really hard to fix production states.
- einpoklum 6y agoOne can't always introduce a multitude of shitlists into different places in the code. This can impact performance and readability; and perhaps even more importantly - if the language used is not high-level with reflection capabilities, you will be counting on callers' participation in calling the semi-deprecated API with truthful "usage key" for looking up on the shitlist. Who's to say nobody reuses authroized keys for expediency?
- donatj 6y agoIn a similar vein but on a smaller scale we have a number of tests for deprecated behaviors in our application code, and whitelist the existing code based on serializing the file name, method name, and the ordered list of parsed token types of the method. That way minor alterations like changing a string or numeric value don't remove the method from the whitelist, but alterations to the logic of the given method require you to fix the issue while you're in there poking around already to pass CI.
- ChrisMarshallNY 6y agoThat's a pretty good idea. I think a key to doing large, multi-developer projects, is true modular design, with opaque APIs, and each module with its own project identity and lifecycle. Not a particularly popular stance, as it means a lot more overhead in each project. It does reduce the need for shitlists, though.
- dhosek 6y agoIf you don't want people to use deprecated code, it's essential to document what the replacement should be. In JavaWorld, this can be done through Javadoc. Just tagging a method or class with @Deprecated and leaving it there is the sort of thing that makes me want to hurt people.
- postalrat 6y agoSeems to me that if you want shitlists to drive development then often there won't be an immediate replacement.
- ritchiea 6y agoThis is literally one of the topics covered in the article and the author advocates for the same solution, documenting the alternative in the error/deprecation message...
- andruc 6y agoIs supporting the article's position frowned upon?
- scott_s 6y agoNo. But when it's not explicitly mentioned as support, it can appear as an implicit contradiction to readers. We also want the discussion about an article to build on what the article proposes, and explicitly tying comments to the article help with that.
- ritchiea 6y agoAgreed. There's been a lot more "commenting without reading the article" here recently than in the past. So when someone comments on an article without anything in the comment that refers back to the article and merely offers the exact advice that was in the article without further analysis or insight: 1. That gives the appearance of a user who hasn't read the article 2. Even if the user did read the article I'm not sure what their comment adds to discussion of the article. For comparison, imagine participating in a conversation with a few friends in real life, friend X says "It's a good idea to write tests for your code," then friend Y replies "It's a good idea to write tests for your code." Usually the way people discuss things is to reply or offer addendums to the original idea. It's not a rule, it's just good conversation.
- chapium 6y agoLove the concept, hate the name. To me this seems like the recycle bin concept. Allow the code to exist temporarily, but encourage permanent deletion. Perhaps a better name could be a "screamlist" or "needstochange" list.
- rightbyte 6y agoTodo list? All arbritary rules like this just makes the process a mess. Also the same people doing the shitlist will most likely be the dug in senior ones responsible for all the shit code being there in the first place and include the wrong things.
- Rafert 6y agoThe Ruby-based tooling has been released as open source: https://github.com/Shopify/deprecation_toolkit https://github.com/Shopify/deprecation_toolkit
- gitgud 6y agoOn a somewhat related note: One of the biggest things I've noticed is that simpler pieces of software are less likely to be deprecated, whereas complex codebases quickly become legacy and abandoned...
- jcelerier 6y ago... windows ? MacOS ? Literally every Adobe software ? Literally every large audio / video / 3d création software ? Every large game engine ? I don't know, I notice pretty much the opposite in the field of software I know - smaller software come and go, but the large ones were there 20 years ago and will likely still be there in twenty years
- necheffa 6y agoSo you'd write a new application using win32? There is a lot of inertia driving maintenance on some of these large products but there is also a ton of deprecation taking place internally relative to them.
- gwbas1c 6y agoOn Windows, almost every "new" API is built on top of Win32. Obviously, if you're writing a simple CRUD application, you'll never need to go down there. (You'll probably make a browser application or an Electron application.) But if you're doing something unique, like a utility, or trying to hook into the OS to make it do something new, you gotta work at the lower level APIs. And, yes, I shipped a product that included a Windows driver this year. It required working at the Win32 level in user-mode to communicate with the driver, and to test the driver. Windows recently added a new API to Windows 10 that eliminated the need for a driver, but we still had customers on Windows 7. Even then, that new API is technically Win32.
- siggen 6y agoI still write new applications in Win32 (or x64). If you want to ensure your app works on as many environments out of the box as possible this is the way to go. In fact, you shouldn’t even depend on DLL version of C runtime as they may not exist on the environment.
- jensneuse 6y agoI will probably remember Sirupsen forever for his move of breaking Golang projects by renaming his github username. This led to go modules freaking out because different project used different name casing for the dependency. He has a popular logging library and go modules are angry when you rename github usernames..
- Chris2048 6y agoDoes go-lang not have "official" repos, including clones of popular repos, to avoid deps on personal repos? Or is this an "unofficial but popular" library?
- Macha 6y agoGo imports are done by direct github URL mostly.
- Chris2048 6y agoAnd to personal repos? Yikes. They should learn from the npm left-pad incident..
- Macha 6y agoYeah, the suggestion is that you should run a caching mirror or manually vendor your dependencies. Other unconventional ideas about go packaging is that new major versions should have a new package name or be available in a subdirectory with the old version existing in perpetuity, and the minimal version selection approach to dependency conflicts. I'm sure these things work well inside Google, but at least for me the package management situation is a big turn off for the language.
- randomswede 6y agoIt is not (quite) as bad as it sounds. If you have built a module-enabled Go binary yesterday with a specific version of an external dependency, you will get that exact version of the dependency tomorrow when you rebuild (IFF you have version-controlled your go.mod and go.sum, which you should). And if someone tries to be sneaky and move the release tag, the checksum will no longer match, and your build will fail. If you want to upgrade the version of a dependency, that is doable (with plenty of toll assistance), but it requires a positive action. Theer is, however, no good solution for the "first use". But, that is pretyt much standard.
- DonHopkins 6y agoIt's better to use a Shiterator, which can yield pieces of shit on demand, lazily enumerate them just in time, and generate infinite streams of shit, instead of allocating all your shit up front, and having your shit together in one place.
- agravier 6y agoThat's elegant dungtional programming but it's not really relevant here as you are supposed to check if new crap matches any of the enumerated fecal matter; how would you do that against an infinite stream of diarrhea? I'd leave this to the type system if possible. And of course use a colon-delimited list for the occasion.
- jokab 6y agoAlso make sure you have enough RAM in your septic tank
- gwn7 6y agohaha so irrelevant yet funny
- ddingus 6y agoYeah, it hits slow too. Read it, started to chuckle, than BAM! Hilarious!
- ben509 6y agoShiterator requires your system is able to consume fibers, though, or it'll dump all your kernels, which is not a pretty sight.
- Terr_ 6y agoIsn't that backwards, though? More green threads increase the throughput of kernels, or at least decrease the processing latency.
- 6y ago
- gwbas1c 6y agoI'm more curious about how things like shitlists are implemented in different languages. Most of my experience is C#, where calling deprecated code triggers a warning. Considering that C# warnings will fail in CI, how would someone do a C# shitlist? Would it require some kind of #pragma, that would stick out like a sore thumb in a code review
- tantalor 6y agoMy preference is when violating the shitlist is a compile/build/lint error.
- ben509 6y agoI think if warnings can't do the job, you'd have to go the route of writing a test that greps the codebase for usages. Maybe a custom linter could also do the trick. The ability to grandfather in existing violations, though, is something most build tools are likely to have trouble with. Maybe another way to do it is to have warnings fail CI, but grep out the known offenders from stderr. None of that sounds elegant to me, but it _is_ a shitlist.
- ivraatiems 6y agoConsider using the [Obsolete] attribute on methods or other entities you don't want people to keep using: https://docs.microsoft.com/en-us/dotnet/api/system.obsoleteattribute?view=net-5.0 https://docs.microsoft.com/en-us/dotnet/api/system.obsoletea... This results in syntax highlighting in Visual Studio and warnings on compilation when somebody tries to use the method.
- jonathanlydall 6y agoThat is pretty much what we do on our C# code base. We treat warnings as errors and use #pragmas in cases where obsolete members are used. When marking members as obsolete we require it be commented with a Jira issue number to make sure removing it is not forgotten. I also strongly encourage that an XML Document Comment is added with a <see cref=""/> to the replacement member which is navigable to by the IDE. Finally, reviewers of the PRs just need to make sure these rules are applied, but it’s easy to spot #pragmas.
- bravura 6y agoA simpler hack is just to give deprecated methods unwieldly and obstreporous names like: `REMOVEME_old_function_name`
- TeeWEE 6y agoMuch better to annotate those functions with @deprecated and allow running in "deprecated allow mode" and a "deprecated fail mode"... Never use profanity in your code, nor in your comments. Lets keep things professional.
- adewinter 6y agoIf you do the above, your prod servers will always run in "deprecated allow" mode because developers continue to use (and add new) deprecated functionality. That's what the article is attempting to tackle. Why not use profanity in code? We're all adults.
- Spivak 6y agoProd should always be running in allow mode anyway for the same reason that prod builds compile out debug asserts and static type checks. Once the code passes review and makes it into prod you've already lost and spitting out warnings on every function invocation will just piss off your ops team.
- adewinter 6y agoRight. So what's the point of having @deprecated tags and a deprecated fail mode if you can't ever use it?
- gowld 6y agoTests.
- Spivak 6y agoOkay, so first you have to have a whitelist of "allowed" usages of depreciated functionality for this to actually work. Anything that makes it into prod goes on the whitelist. All dev environments, QA, and your automated testing run with fail mode on and trying to push code that adds to the whitelist should set off alarm bells. From there it's up to the team / business if it's worth doing it anyway.
- mcv 6y agoEven in a single-team project, I've ran into the problem that I'm refactoring some old code while others keep adding to it. Some way to have to cause failing tests sounds like a great idea.
- closeparen 6y agoWe use Bazel’s visibility settings this way. More often for controlled beta rollouts than for deprecation, but same principle. You can make it a compile time error to import a package that isn’t for you.
- telotortium 6y agoYes, this is exactly what we do inside Google - it works even better when (a) everyone uses Bazel for everything and (b) all the commonly-used languages have dependencies checked at compile-time.
- jbackus 6y agoAnother cool way you could do this: Ruby methods can ask for their caller_locations. They work like this: # Source $ cat example_for_hn.rb def foo bar end def bar baz end def baz (c1, c2) = caller_locations.first(2) puts "Parent caller: '#{c1.label}' in '#{c1.path}'" puts "Grandparent caller: '#{c2.label}' in '#{c2.path}'" end foo # Demo $ ruby example_for_hn.rb Parent caller: 'bar' in 'example_for_hn.rb' Grandparent caller: 'foo' in 'example_for_hn.rb' So, you could define a method decorating class method like so: module Shitlist def shitlist(method_name, whitelist) original_method = instance_method(method_name) undef_method(method_name) define_method(method_name) do |*args, &block| call = caller_locations.first passes_whitelist = whitelist.any? do |label, file_pattern| call.label == label && call.absolute_path.end_with?(file_pattern) end unless passes_whitelist fail "Shitlisted method! Permitted callers: #{whitelist}" end original_method.bind(self).call(*args, &block) end end end and then extend classes with it to use the decorator: class Example extend Shitlist def not_on_shitlist qux end def baz qux end def qux puts 'Only some methods can call me :)' end shitlist :qux, 'baz' => 'shitlist.rb' end If I run this example (full source: https://git.io/JLOdV https://git.io/JLOdV), the non-whitelisted caller throws an error: $ ruby shitlist.rb Only some methods can call me :) Traceback (most recent call last): 2: from shitlist.rb:44:in `<main>' 1: from shitlist.rb:25:in `not_on_shitlist' shitlist.rb:13:in `block in shitlist': Shitlisted method! Permitted callers: {"baz"=>"shitlist.rb"} (RuntimeError) --- Of course, you might not want this hijacked method with tracing inside something performance critical. You could always configure the implementation to be a no-op in production.
- reader_1000 6y agoI don't remember the exact project or where I read it but one project added delay (using sleep function) to depreatected methods in order to discourage people using them. After every release, they increased the sleep time so that at one point it became impossible to use it without noticing it. Although I don't know if it is possible to do this for new code trying to use deprecated function and have old code use non-delayed one, I think it was a nice trick.
- ddingus 6y agoThat is hilarious! Also, a fair percentage evil. Nice trick.
- jimmaswell 6y ago100% evil and pretentious. Not every dev team has the free time to go rewriting everything to the whimsy of library devs that can't help themselves from incessantly shuffling everything around every week. This is how you encourage people to never update and keep security vulnerabilities in the wild.
- laichzeit0 6y agoReally? That’s basically the premise of every JavaScript web framework.
- izacus 6y agoWe're talking about transitions where the library authors deliberately want to (and have good reasons) to migrate whole organization over. It's not they who are pretentious in your scenario ;)
- hchz 6y agoThis is not a decision a library developer makes in a reasonable Engineering organization.
- javier10e6 6y agoAs software goes, years later there is going to be a shitlist_v_1 (bad classes) and a shitlist_v_2 (very bad classes) and on and on
- notsureaboutpg 6y agoGreat post and I loved the "Monthly Napkin Math" section on his site as well. Always a treat to discover new resources like this :)
- ExcavateGrandMa 6y agopython is a slow shit...
- fduran 6y agoThis is a good example of "Organization as Code", where you are trying to influence a non-trivial change (often "cultural") by code. Frequently it's better to change behaviour this way than say, holding meetings and presentations. A typical example is "we want our developer to write more tests" but there are few existing examples of test code to look at and when you write a test it takes forever to run, so you fix this by fixing the underlaying issues (which is bad or non-existing code), rather than making developers attend TDD presentations and just talking about a "test culture" for example.
- acbabis 6y agoI've experienced this first-hand. Joining an existing project and being put on test-writing duty very quickly turned me into our team's chief advocate of writing testable code.
- weeboid 6y agoVery difficult to sell something named "Shitlist"
- leblancfg 6y agoVery easy to give it another name
- deleted 6y ago[deleted]
- awinter-py 6y agoI love his specific examples. > Make sure that a certain datastore is only read from in a certain context ... Ensure fallbacks for all uses of a secondary data-store ... joins between tables that have no business being joined I wish linters / typesystems were extensible enough to do this kind of domain-specific checking. There's a new generation of static analysis that's much more focused on architecture or business rules and is less about code-in-the-small concerns like class methods or operator compatibility.
- anonygler 6y agoThis is accomplished at Google using the build system. Your visibility declaration tells the build system what's allowed to depend on a piece of code. When deprecating old code, you can shut that door with a whitelist and gradually force consumers to migrate. I really like how code is managed at Google.
- mleonhard 6y agoThe article shows ways to prevent new dependencies on your deprecated library/service. This is half of the problem. The other half is efficiently removing the existing dependencies. Your team could change the other teams' code to remove the dependencies. This is usually inefficient. You will waste time learning their code. They may drag out the code review process for weeks or months. Some teams may refuse to accept your changes and use your service deprecation as political capital to demand more headcount. They may even lie to their managers and claim that the dependency deprecation is justification for a rewrite that they want to do. There is a technical solution that can help with this social problem: AUTOMATICALLY EXPIRING DEPENDENCY APPROVALS. Configure your library to allow only existing systems to use it, and make them all break on a certain date. Then, instead of forcing the other team to move, they have to move or their build breaks. And if they want to delay turndown they must convince you to change your code. Without automatic expiration, they can delay turndown by simply ignoring you. Some teams may wait for the dependency expiration, lie saying they didn't know about the turndown, and then demand that you delay the turndown and give them more time. You can work around this with a two-phased turndown. First create a new version of the library that allows only existing clients. Give the library a hideous name so code owners will want to remove it. Example: Deprecated_LiBrArY_YoUr_BuIlD_WiLL_BREaK_oN_20200601_LOL_Were_sERIUS_YOLO_exxtensuns_COme_frun_SVP_DaniELs_OnLY. Then set the existing library to expire in a week and email all users. They can easily switch to the new hideously-named library and in the process acknowledge that they know that their build will break at the specified date. TLDR: Use expiring white lists so you won't get ignored. Rename your deprecated library to something hideous to motivate code owners to migrate away from it.
- csutil-com 6y agoI love that C# supports deprecation as a language feature and allows to mark an obsolete method as only usable by other deprecated methods, which is the same as the shitlist approach here in the end