Shitlist Driven Development (2016)
sirupsen.com
sirupsen.com
Also, a fair percentage evil.
Nice trick.
That broken build won't be pushed to production and affect users, but invisibly slowing down a production service and hoping someone notices via monitoring will probably pass tests, build fine and be pushed to production where it will affect end users. I absolutely want to work in an environment where the build fails rather than people play childish tricks to punish my users because I used a deprecated function, and hope I notice.
This seems like a slight variant of SDD: dickhead-driven-development. Clever, but your coworkers still hate your guts.
If you have objects your API functions return that API consumers will use, you want to make sure the objects don't suddenly grow in size, since that would eat into the memory that the consumers expect to use. So, you'd pad those objects with unused fields, and then if you need to add a new field, you can just use that padding area.
Something similar applies if you're sending objects over the wire, since you may not want to increase the size of the message later.
He’d politick to transfer in new teams for the sole purpose of building a supply of cannon fodder. Staff up in good times, and purge fodder when layoff targets came to protect “his people”.
A few months later that department was practically begging us to convert their excel document into a database project.
Doing stuff like this seems creative and awesome at the time, but it breeds vulnerabilities something fierce. It also creates a nightmare for maintenance.
I would suggest a different approach of figuring out how much that Excel doc was costing the company every month, how much the company would save if the doc was converted to a real data service and Web front end, and then present the comparison at a meeting with management from that department - give them a chance to sign on before you take it to more senior management.
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.
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.
The article in question is usually not the topic of conversation on HN. It is better to consider it to be the launching point for the conversation. Some will discuss the article, certainly, but you can expect the conversation to branch out very quickly to related items (especially if those items are mentioned in the article). While it's probably not a good idea to discuss the plight of African elephants in these threads, drawing attention to a minor, but specific point in the article is perfectly cromulent.
In this case, the post has value. It does two things. First, it draws attention to a subtopic that was only an aside in the article. (One I happened to miss the first read-through.) Second, the phrasing in the article is somewhat general and vague about this, and the post is relatively more specific and direct.
And in the case where people don't read the article and just read the headline, many comments have nothing to do with the article, and end up discussing a less interesting misconception of the submitted article's title/headline.
It's great to be generous with our interpretations of comments here on HN. But I would like to push for a higher standard of discussion, which has always been the draw of this community, particularly for technical articles (and this is actually a high quality technical article!). So I don't think I'll ever be happy being generous to a low value comment that re-states an opinion from the submitted article without adding anything or even without adding much.
Deprecated means “still supported but its use is discouraged.” It does not mean “no longer supported.”
C++/Wikipedia uses the definition of the original poster, while Java's @Deprecated annotation literally has a `forRemoval` boolean value attached. Personally, I am used to how it is used in semantic versioning where it is just a helpful hint for what the next release's breaking changes may be while introducing the migration behavior if possible in this release. This allows for clients to incrementally migrate instead of making it all or nothing.
Now that I've been programming for a nontrivial number of years, it's happened enough that I no longer trust future me to understand anything clever that I do, so I spend much more time structuring code to require minimal context. Failing that, ample documentation, because future me will appreciate having an essay to read much more than current me wants to write it.
Seriously.
I'm now more obsessed with documentation and the kind of code structuring you mention and have come to hate, viscerally, projects that lack documentation.
As a potential aside, I once took a course in ASP. We had a bit of time before the class started and the tutor had a chat with a couple of us. Some huge government projects were in the news for having failed and gone well over budget. He told me he'd worked for the company behind them and that they used to obfuscate the code just by stripping out all the comments so the government had to keep going back to them for work.
I think of that any time I hear "but the code is the documentation"…
It's so helpful when I do it that I probably ought to do it more, and if there weren't so much overhead I'd probably do it everywhere.
You need to communicate clearly with a number of coworkers, the most important of whom is future-nerdponx. Sometimes future-nerdponx is smarter than you, and sometimes future-nerdponx is dumber than you, but either way they know where you live, and their misery is your misery.
def shiterator():
while true:
yield random.choice([
"sinker", "floater", "firehose", "steamy heapy",
])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.
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.
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.
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.
> My point is that deprecation is by its very nature going
> to hurt something that isn’t prepared for its demise.
TFA describes techniques for deprecating code paths that do their best to notify and prepare the folks working with that code.You're describing the end of Life of Adobe Flash that hits at the end of 2020.
Deprecation is different from EOL (Flash has been deprecated for some time now), and deprecating/EOLing a creation tool/product line has little similarity with tooling to deprecate internal APIs gracefully.
Although I do think that you and TFA agree on how difficult it is to deprecate functionality that folks rely on, it seems like you're talking about two wildly different facets of that statement.
> 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.
We used this technique to great effect at my previous day job, though under the more neutral name of "ratchet".
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.
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
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.
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.
The maintenance of giants is tied to their importance rather than the complexity of their codebases. SQLite is no less likely to be around in the future because it is simple.
Codebase complexity also isn't just about size. A huge, well-written codebase is still more likely to stick around in its current form without requiring a total overhaul.
I can list 10 billionaires in the world, but being a billionaires is not normal...
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.
> 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.
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.
# 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), 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.
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
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.
This results in syntax highlighting in Visual Studio and warnings on compilation when somebody tries to use the method.
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.
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.
It was personal because the idea didn't get a lot of attention when I brought it up.
And why is it an organizational smell?
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?
> “shitlists”: a whitelist of deprecated behaviour. Existing deprecated behaviour is OK and whitelisted.
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.
Never use profanity in your code, nor in your comments. Lets keep things professional.
Why not use profanity in code? We're all adults.
You just answered your own question there.
I understand that naming features as not supported also makes sense. But to me that is what feature toggles are for.
Or is this an "unofficial but popular" library?
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.
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.