# Odysseus That's a true story, by the way. Fucker had me set an alarm to get fired. Welcome to my endless black void. I'm not under a non-disparagement agreement. So let's disparage. We'll talk about moronic code and the stupidity that leads to it. with me are 2 straw men: Brian, who has a brain in his skull. and Odysseus. Also, quick aside: you'll notice I didn't pay for voice acting on this one. I'm at least as upset about it as you are. Occasionally, comments insist I should use my own voice. I think they think my usual voice actor is TTS. 1) link please 2) notice how my voice is the parody voice you put on when you’re making fun of someone for being a nerd. Human or not, we all prefer James. Anyway. On with it. ## the Pain Zone Like a *scrub* who just buys a hallmark greeting card, let me borrow someone else's superior writing, it expresses what i'm trying to say better than I ever will. Ludicity is a better writer than I can hope to aspire to. [https://ludic.mataroa.blog/blog/get-me-out-of-data-hell/] [alt va] It is 9:59 AM in Melbourne, 9th October, 2024. Sunlight filters through my windows, illuminating swirling motes of dust across my living room. There is a cup of tea in my hand. I take a sip and savor it. I text the other senior engineer, who unlike me is full-time, on the team: "I'm ready to start at 10", as is our custom. The minute hand moves. It is 10:00 AM in Melbourne, 9th October, 2024. The sun is immediately extinguished and replaced by a shrieking skull hanging low in a frigid sky. I glance down at my tea, and it is as blood. I take a sip and savor it. I text the other senior engineer on the team: "Are you ready to enter the Pain Zone?", as is our custom. He then goes on to describe the horrors he witnesses. This speaks to me, because every day I open visual studio, and witness unfathomable atrocities. And then it's my sisyphean task every day to mitigate them. ## organization Let's lay some foundation here. (Already doing something I didn't get to do in the day job.) A program is a set of instructions. We write "code" to be semi-human-readable, yet easy for a machine to compile into a list of instructions. if you're just writing a program to print hello world, might as well write it all in one function, all in one file. As the program has to do more complex things, you need more code, so the need for organization grows. There's a trend in frontend world to organize your files in the supidest way possible: by file extension. Also known as... the organization your file browser can already do. Usually this is rationalized with, as ever, a barely analogous metaphor. Instead of keeping your laundry in a heap, you sort it, and put it away. That way you can find what you need. But creating folders for file types is like sorting your pile of laundry by, idk, color. You're deciding what you want to wear today, so you look through your entire black collection for a shirt, decide you don't like any, look through your entire white collection for a shirt, decide you want to view more of your options, look through your entire blue collection, and so on. This is what it's like when you know you have a control, so it must have some HTML, but you don't know whether it has its own css, or if it has its own javascript, or if it's linked to anything else, which might have its own html or css or javascript - you're stuck looking through everything, because the organization is useless. This is what results when someone theoretically likes the concept of organization, but never has to suffer their own system. I never had to live in front-end world. What we do in backend world is equally moronic, though. we make a namespace for interfaces. put a pin in that, that's another stupid thing. but first, let me tell you about some namespaces. ## namespaces Brian has heard of MVC - Model, View, Controller - and he separates his code into a Models folder (for data), a Views folder (for rendering), and a Controllers folder for other functionality. Odysseus has also heard of MVC, so he has a Models folder. But he doesn't know *what* a model *is*, so the Models folder has, within it, 2 folders: `Data` and `DTO`. DTO is for request and response objects, and Data is for Entities in the database. It's perfectly fine to have DTOs, and to persist your data in a database - but in addition to `Models/Data`, there's a `Data/` folder. It's clear that Odysseus doesn't know what "Data" *means*, which is why `Data/` contains no data, only a database context. Well, that and an Interfaces folder - every folder that isn't itself an Interfaces folder contains an Interfaces folder. One Interfaces folder *may* make sense. But when the same name is seen over and over and over scattered across countless spaces, this is the opposite of organization. ## interfaces Anyway. Interfaces. what *is* an interface? in C#, to avoid the diamond problem, [other] "what's the diamond problem?" when you inherit from 2 objects which each inherit from a shared third. the compiler doesn't know which implementation is the one you're supposed to have. Or worse, it guesses unpredictably. in C# you can inherit from no more than 1 object. But you can implement as many interfaces as you like. Brian sees a need for an interface when more than one concretions exists - this enables him to think about the functionality contract on an abstract level, rather than whatever concretions may be out there. Odysseus feels like he ought to have interfaces, so he writes lots of them - a specific one for every class. Now, every time he writes a function on an object, he has to copy the signature over to its interface. And what did all this extra work give him? Nothing, it's just that object's public interface, again, but with more of its information obscured. It also doesn't get him the ability to think on a higher level of abstraction, because every implementation will have its own interface method reflecting its own peculiarities. Whenever Odysseus passes around an interface, or calls an interface method, it's the specific method for a specific implementation. In other words, Brian uses interfaces to help himself reuse code. Odysseus uses interfaces to help himself write twice as much one-time-use code. Does it get any worse than single-use code? ## blank classes **of course it does**. ZERO-use code! I'm an adherent of object oriented programming. There's a growing bandwagon of haters, but I assume they're all javascript programmers so they don't count. either way, rather than arguing for the paradigm that aims to bring you good things, i'll argue for the good things themselves. Then we don't have to agree if OOP is the path to code reuse. Code reuse is a very important core value you should be working towards. All else equal, a solution that solves more is better. A solution that is broken by less is better. A blank class is a solution that solves nothing before even being made. It's useless by design. Here's a fun ripgrep search to run: ``` rg --multiline "class [^\s]+[\s\n]*\{[\s\n]*\}" ``` for every hit, take a moment to contemplate that you have to maintain this codebase, and self-medicate accordingly. I have mine here. [shot] What's equally as useless as a class that conceptually goes nowhere? A class that's conceptualized backward. ## [REDACTED].cs probably shouldn't mention the actual class. When Brian creates a base-level class, it's as generic as possible. Fortunately C## already includes `Object` as the class from which all others derive. As Brian derives classes, they get more specific. We all love the car analogy for classes. So let's start with `Object`, and then derive from that a `Vehicle`. Then to get more specific, we might want a `Car`. this way we can be separate from an `Airplane` or a `Bicycle`. Odysseus sort of understands that a derived class inherits from its parent class. So if we're trying to figure out pathfinding for our `Car`s and our `Bicycle`s, Odysseus might then acquiesce to inheritance, and give `Vehicle` a function to determine a path. But wait, routing through the air is somewhat different from routing on the ground, right? So he creates `GroundVehicle` for `Car` and `Bicycle` to derive from, and `AirVehicle` for `Airplane`s. The problem, analogous to what I've seen in the past, is that now Odysseus writes his routing logic for cars and bicycles - you just hop in and follow the roads all the way to your destination. Then he writes his routing logic for `Airplane`s - most people don't own their own, they buy a ticket on someone else's. So the routing logic for `Airplane`s centers on finding and pricing available transport, and minimizing your traversal. That winds up in the logic for `AirVehicle`. Then we come to another `GroundVehicle` - the `Train`. Now all the routing logic for this particular `GroundVehicle` is effectively identical to the logic in `AirVehicle`. Odysseus maintains both versions of the identical logic for a while, and then solves this problem in the worst way possible: he makes a new class, `RoutableBase`, then has `Vehicle` derive from it. It's designed backward! * nothing will ever be routable other than a vehicle * there might be vehicles where routing is irrelevant. a skateboard is a vehicle, right? * routability is a feature that vehicles have, as opposed to vehicles being a type of routable and now we've set a precedent that things that *should* go in `Vehicle` are going in `RoutableBase`. so `Vehicle` is left to stagnate and be forgotten - then future vehicles just bypass it. Then in the future when a vehicle's ability to transport cargo becomes relevant, all of the cargo logic is put into `RoutableBase`. Don't worry, the real life example is even stupider: not only is it conceptualized backward and named after a fraction of its purpose, it's also named after a brand! ## obscuring pragma regions Another way to obscure information in a class file: ```c# #region Private/Protected Fields #region Private Fields private readonly object foo; #endregion Private Fields #endregion ``` I can only assume this is the aftermath of a tool written by morons for morons. Yet again we see someone doing something *like* organization, to accomplish the opposite - more barriers to stop the reader from finding what they're looking for. Pragma regions let you collapse code. I assume it shows up like this in the code over and over because that has the feeling of putting stuff in a drawer. I defy you to imagine any other benefit. Any tool that wants to know what fields are private or protected can read the declaration. Certainly there's no tool that would need to find a category that covers both private and protected fiels before looking for private fields. And if there is, throw it in the trash rather than letting it touch your employer's code. I adamantly refuse to believe that a person wrote this for the purpose of organization; the only way to see the collapsed pragma region is to be in the file. Which means you want to see the relevant information. ## living life on a coin's edge everything in software is a double-edged sword. Alright actually, everything in software is a tired cliche, made harder to understand with an absolutely shit metaphor. But it is engineering. There's always tradeoffs. And that's why all day long we make decisions about what the computer should do. But honestly? most of it is taste. It doesn't matter. [carl from aqua teen hunger force] None of this matters. The only wrong answer is no answer. I'll admit: I hate to see someone dead set in one perspective, so I tend to find myself taking sides I don't actually care about. Here's an example. [https://www.youtube.com/watch?v=hxGOiiR9ZKg] CodeAesthetic is extremely popular. And I have to hand it to him, I'm a massive fan of his VideoAesthetic. But this video is where he compares doing inheritance wrong to doing composition right, and uses that to justify the cliche "prefer composition over inheritance". So of course CodeAesthetic is a fan of the current fashion: dependency injection. [https://www.youtube.com/watch?v=HOSdPhAKupw - "it's great, until it's not great"] So, Inheritance? or Composition? Just pick either one and stick with it. Of course Odysseus was able to thread the needle and bring us the worst of both worlds. you've seen these lines in every asp.net web app you've ever built, right? ```c# var builder = WebApplication.CreateBuilder(args); ``` Var builder is web application dot create builder, followed by The Great Wall Of `builder.Services.AddScoped<>()`. you want an instance of a class, and you're hoping this way the memory is handled for you. Odysseus thinks this means that whenever you need an instance, you have it dependency injected - whether it should be global, [note] in so far as c## has globals, they'd be a static class a singleton, or not. So *inevitably* things sneak in like a file called `GlobalFuncs` - a class that ostensibly is a collection of global functions, right? other classes don't need the framework to instantiate it for them, just make it static! It's not like it has properties, right? ...oh of course it does. ```c# public string foo { get { return "bar"; } } ``` [straw odysseus] well of course it's a Field, `GetFoo()` is a java thing, and if it was a const this wouldn't be a class of global funcs! 1, that's dumb. 2, **why does it need a goddamn interface then?!** And of course there's another one; the db connection string, which it just reads off the Configuration, as it should. wait, this is new to me, what's a- [redaction bleep] functions for it.. oh fuck. This is the experience of looking through a codebase like this. we have a constructor that gets injected with and then saves its own instance of classes that should each be static; the global functions collection, the configuration class reference, and... IDBContextFactory, made generic of course, so that this one is for... BazDBContext. If you're familiar with entity framework, BazDBContext is where you add your `DbSet`s. Great. And it has to be made through a factory, fine whatever. But. Let's look at what's done with it. ```c# //redacted, obviously public Foo(IGlobalFuncs globalFuncs, IConfiguration config IDbContextFactory db, ) { _globalFuncs = globalFuncs; _config = config; _dbConnectionString = db.CreateDbContext().Database.GetConnectionString(); } ``` **for fuck's sake** it names the context factory db (usually the name you give to your db context), then has "db" create a db context - a task that involves actually connecting to the database, then look at the underlying Database information... to pull out the connection string! So don't worry, we don't pass a database context, we create one... which we then only use to look at the information not relative to any context, and then we never care about the db context factory again... all to find a value that of course should be, and coincidentally is, in configuration, which we also have! So what does this have to do with living life on a coin's edge? Let's speculate about how this happened. I bet: Someone said "i need the connection string - I can get that out of the DB itself, of course." So he did. And it never occured to him that this is a dumb idea; if you have 1 configuration value in the world, would it not be the connection string? and if you have a connection string, why would it not be a configuration value? Then someone said, "I need a configuration value." So he brought in configuration. Then placed it *right next to* the spot where he gets the connection string out of the `dbcontextfactory`. A second chance to fix this, failed again. And then anyone who touched this in the future resigned to the fact that presumably it must be needed for some reason. I'm guessing at least 2 people, maybe more, couldn't make a decision about which approach to use, and so allowed this cruft to fester. this is what happens when there's no one with the authority and skill to make a decision. Ideally this person would call themselves a software architect. Or at least a senior developer. Either way, they deserve a pay raise. ## Primary Developer how did we get here? first a more fundamental question. Who are we? Usually, a bunch of clowns with monkey wrenches, plus 1 Primary Developer. I once had a job I loved. Fully remote, of course. their headquarters was in a different timezone, so I didn't have to wake up until 11:00. At this place, we had The Primary Developer. She was there the longest. She had the most skill. She knew the system like the back of her hand. The team had a custom slack emote that was just "Ask [Redacted]". Then that company got bought by a much worse one. I was placed on a team made up entirely of new-to-me people. On that team, there was another Primary Developer. He was the one who knew the system the best, had worked on it the longest. Management answered every question by referring you to him. Then the purchasing company laid off everyone from the company they consumed, including me. I joined another place - and they had their own Primary Developer. They, similarly, had a custom slack emote that was just, "Ask [Redacted]". These people are the backbones of their respective teams. Pillars of the company. Notice I call them "Primary Developers" - a term i've made up. Because they aren't "Senior Developers". "Senior" developers cost more money, so most organizations that don't consider themselves "tech" companies don't have them. And if they do, they're not high level programmers, they're low level managers. This is bad. Software is complex enough that you need someone who understands it and yet can spend all of their time organizing how other people work on it. cufflink-wearing-motherfuckers like to think *they* can handle all the organization, and real workers can just bang 2 rocks together until some money gets printed. They can't, inevitably someone has to have *some* kind of understanding of *everything* to provide useful organization - so with no software architect, a Primary Developer emerges. And now you're relying on production and organization, which is 2 people's jobs, and you're trying to pay the lesser of the 2. If you have someone who tolerates this indignity / pay cut, inevitably all development centers around them - so other developers inevitably realize that they're not engineering solutions, they're just trying to support the Primary Developer. Now your engineering team is only getting one brain's worth of engineering. ## festering cruft 2: comma path separators Let me propose an even worse scenario: what happens when you have a scrub with a senior title who wrongly believes himself to have the skill of a primary developer? Here's another true story: at an unnamed point in my programming JOURNEY, there was a bloated configuration file. why do I say "bloated"? Put a pin in that, we'll come back to it. among this labyrinth there were many paths. Paths to system utilities, usually. But for some reason, to separate directories... rather than windows-style backslashes, or every-other-platform-style forward slashes (by the way: this is a backslash. this is a forward slash. Get it right.) ...instead of slashes or backslashes, it used commas. It used them, consistently, throughout the configuration file. meaning dozens of times, it stuck to this weird custom path format. why? [note] i don't remember the precise wording, but it was this insulting [odysseus] well because when we switch between linux and windows, they don't use the same slashes. "so convert them." [odysseus] no, no, this is just easier. "but surely you *have* a path-parsing class, somewhere..." [odysseus] trust me, you wouldn't understand. so now because some nepo-baby can't figure out `if windows string.replace('/', '\\')`, we're all condemned to this lunacy. and for the record, i was wrong, they don't have a path-parsing class. They copy and paste the path parsing code wherever they need to parse a path out of the configuration file, sometimes with little differences - must have gotten updated in one place, then going forward the new version got copied and pasted. ## festering cruft 3: gone but not forgotten I don't begrudge non-nerds for not understanding git. I don't begrudge any non-programmers for not knowing any version control. One day, like prometheus, I'll bring this fire to those mortals. For now though, if you write software, you had better know git. And if you don't know git, git good (scrub) Brian has no fear when it comes to deleting code. It lives on forever in the repo. And if the organization has an inexplicable habit of moving to a new repo once or twice a decade, then whatever was left behind in the 8 year old commits is obviously forgotten anyway. Odysseus, however, is afraid to delete anything. He hoards code - worst of all, usually still active! commenting it out is one thing, but when you're so afraid to delete that you don't *extract*, there's a serious problem of myopia. And I expect any person with a brain to realize, "wait a minute, this incomprehensible trash fire is so bloated that I keep getting turned around and distracted when I'm looking for something to understand... maybe I shouldn't create more of it?" So let's get back to that configuration file. When is a configuration file "bloated", or otherwise too big? when it contains configuration for stuff that has since been separated into its own microservice. When your configuration files - and yes, i do mean appsettings.json *and* the .development version *and* the .production version - are all committed to source control, you can't not know that you won't need your configuration values anymore. But just like no one can make a decision about an approach, people are afraid to delete anything. Because the decision makers don't understand how git works. So they keep *everything*, just in case [odysseus, in unison] just in case ## festering cruft 4: forgotten but not gone The above is what happens when an underling tells a senior there's a better way. What happens when an incompetant senior then fixates on something shiny and new? the project hastily transitions. If the project had competant planning, changes like this should be doable. Of course, when odysseus is in charge, there's plenty of code that's dependent on vendor-specific code. So you can browse through the code currently running in production and see small remnants of what was once the New Hotness that would solve all our problems. Technologies that haven't been applicable. Frameworks that didn't help. Saviors that have failed us. It's like trudging through the ruins of temples to dead gods. ...yet still having to unclog the toilets. Rest in peace, rabbitMQ. I'm sure you're doing great things elsewhere. Rest in peace, 4 different forms of in-house TCP packet structures. You certainly are not. NiFi is our new god. Surely it will solve all our problems. ## Looks Good To Me prs Quick question, and even the managers might know the answer to this one: what's one way you can increase the quality of the code one of your programmers produces? have another programmer check it. We call this a code review. Github enables it with its pull requests. Brian reviews the code for issues, and requests changes to improve the code. Odysseus sees that code is stuck in review, so he jumps in and approves it. In an environment set by Odysseus, it's inevitable that all pull requests are "looks good to me PRs". That is, every pull request gets blind approval. Now that PRs aren't actually a code review, they're just another step slowing down development. Yet another scapegoat for the specter of "not enough time". ## the eternal scapegoat Soulless business husks love time. It's the perfect scapegoat. [other] We can't do anything experimental, there's no time. We can't fix that old problem, there's no time. Don't do it right, do it fast. Faster faster faster! Management is holding their inability to schedule against their underlings, then making unreasonable demands of them to obscure it. I don't know if it's giving them the benefit of the doubt or not to assume they're bad at scheduling, or they're lying about being bad about scheduling. I tend to assume they're lying. But even if we assume they're being honest about their incompetance... just ask a speedrunner: nothing wastes time quite as bad as trying to save time. Or if you prefer, there's a saying: [note] H.K. Williams, sometimes attributed to Ben Franklin. failing to prepare is preparing to fail. when you're perpetually behind schedule, (or at least, told that you are), you take sloppy shortcuts to catch up. This isn't sustainable - the reason work takes longer is to do it right, and when I say right, part of that means that it can be worked on again later. Shortcuts can't. Trying to figure out what temporary measure they were for, and how to do it properly, takes as long as the original task should have. This is technical debt. Rather than paying his tech debt - i.e., fixing it, odysseus carries around the aftermath of his shortcuts like an albatross. This is cruft. [https://en.wikipedia.org/wiki/Cruft] ## House of Cruft When noticing an issue, Brian takes the time to fix it, and fix it properly. Odysseus reasons, "this is only temporary, let's just put a bandaid on the problem for now." This is how you get 3rd party binaries in your source code repository. This is how you accidentally get output committed to your source repo. This is how you get massive blocks of commented out code, leaving a function just returning false with a //TODO at the top, with 0 references anyway. [commentblock.png] whenever anyone says "this is temporary", they are wrong. Bet on it. this is the Myth of Temporary. you know how painful it is to rip off a bandaid? in my opinion, not very. in software it's more painful to replace a temporary hack. Now imagine that you find a temporary hack, and dread fixing it. That hesitation is made even more daunting because you realize you'll have to fix another one, too. And another. And another. When you're fumbling blindly in the darkness and taking every shortcut you can imagine *from moment 1*, you aren't building something and then putting a bandaid on top.. you're creating a world that's bandaids all the way down. [the reference: turtles all the way down] ## myth of permanent But don't worry, on the other side of the coin is falling for the myth that anything is permanent. An example. ```csharp private async Task> GetLast8PasswordHashes(Account account) { var spec = new FetchByAccountIdSpec(account.Id).And(new FetchByReasonSpec(AuthorizationHistory.ResetReason.normal)); spec.AddOrderByDescending(x => x.CreateDate); spec.EnablePaging(8, 1); var pagingDto = await AuthorizationHistoryRepo.PageAllAsync(spec); var AuthorizationHistories = pagingDto.Rows; return AuthorizationHistories; } ``` Names and numbers have been changed for anonymity. 3 things about this function are disgusting. the Spec pattern, put a pin in that. [pinned note on side of screen: Spec pattern] the authorizationHistoryRepo, I'll get to that too. [pinned note on side of screen: AuthorizationHistoryRepo] [pinned notes drawer slide away for now] and the fact that it's got a hardcoded number 8. How absurd would it be to have this function, and then learn "oh actually we need it to check the last 9 password hashes" - by hardcoding the 8 in, it's now useless for 9. There's a saying in software engineering: there are only 3 numbers. 0. 1. and Many. That wasn't followed here. ## search specs stink [grab pinned note: Spec pattern] Have you ever heard of a Search Specification Object? If so, I hope you're braver than me, and responded with violence. Let's go back to that example from above. Here we see multiple dedicated classes to avoid writing linq queries. What if I told you linq queries were deemed a bad code smell, so we have to create work for ourselves to avoid them? Now we're ignoring the language's built-in solution to instead create our own version, which has the highest ambition of being equivalent. Naturally, for now, our version is incomplete. `AddOrderByDescending` doesn't return `this`, so that fancy imperative syntax isn't realized - we've done just enough work to be neither declarative nor imperative. ## I, Repository [grab pinned note: AuthorizationHistoryRepo] Question. How many databases does your app connect to? Probably one, right? unless what you're doing is moving stuff between databases, "an app" has "a database". Any fanciness like colocation or replication ought to be handled on the database's side. So how many connections should one app have to that one database? It's probably a question about performance and thread safety, right? maybe one, maybe a few. How many connections to a database should one object have? The question is *absurd*, is it not? Odysseus heard about the repository pattern, and decided to sort of implement that. So there's a generic `IRepository` that takes a type parameter of a database entity. Sure, it collects all of the `CRUD` operations, as one might, except! we're using my beloved entity framework. *that's* the repository. the repository object is just a wrapper that allows you to use *some* of the dbcontext's functionality. and just for extra funsies: there's `IRepository`, because someone heard interfaces are good. It is implemented by exactly 1 class, meaning it's pointless. That one class is `BaseRepository`, which is the "base" for exactly 1 class, meaning it too is pointless. But at least that one class has 0 code of its own. This results in a frequent fuck-you to a new developer trying to join the wavelength of madness the previous devs are on. That way when you look for what a function does, first you accidentally go to the interface, which looks back at you mockingly with no implementation visible. You find yourself at The Great Wall Of `builder.Services.AddScoped<>()`. You see a line: ```csharp services.AddScoped(typeof(IRepository<>), typeof(OdysseusRepository<>)); ``` You check out `OdysseusRepository`... [RepositoryPattern.png] and the class has no fucking members! ## open/closed I've said before that what makes scryfall great is that it's software *made* to be *used*. Typically, if you're being paid well to write software, it's something your boss has ordered you to write so someone else's boss can order them to use. that is, no one involved at any point actually *wants* this software to exist. When no one cares about what the software can do, in the future or now, you'd better believe no one cares what the software did in the past (including the people who wrote it). That's the real reason the open/closed principal is so popular in commercial software. Quck recap: Object Oriented programming was the revolutionary idea that [show a book, titled "maybe we should reuse code sometimes! here's how"] maybe we should reuse code sometimes so here's how. With it emerged the SOLID principals. [on screen: word SOLID. have it normal/horizontal, then slide the letters diagonally so we can show what it stands for.] solid stands for Single responsibility, Open/closed, Liskov substitution, Interface segregation, Dependency inversion. Hot career tip: memorize that for your next interview. all of those are pretty common sense and generally good ideas, except the O. [fade out lines other than open/closed] The open/closed principal is the idea that you're less likely to introduce bugs in new code than you would be in old code. Which is nonsense. If you create X bugs in Y lines of code, what force would make them less likely to be buggy if they're new lines rather than revisions? I submit that it works the other way around. If you're forced to read and understand the code that already exists, you're less likely to do something that doesn't mesh with the rest of the codebase. And you get to be the second line of defense against whatever terrible ideas snuck in the first time. And you're able to benefit from what was learned before you - very probably including cases where the most intuitive solution is wrong, and how that bug was already fixed. But to do that you have to *learn*, which means allowing more unwelcome knowledge of this wretched codebase to infest your brain. I can empathize, that's a disgusting scenario. However, for once these cursed thoughts *pay rent*. so instead of the open/closed principal, I would like to introduce an apparently world-shaking idea: we should work together. [ME2, Legion: "cooperation furthers mutual goals."] Instead of objects being "open for extension and closed for modification" - we have git now. Extend it when you need to extend it. Modify it when you need to modify it. whatever you like, just write a clear commit message. ## the King of IT This one is depressingly widespread. Odysseus issues new hires an affordable laptop. It's set up and ready to go... assuming you only use it to read email. Odysseus is very proud of his ability to remotely administer a PC, so he's applied all the security features he's learned about. This results in a software developer getting a laptop and being unable to install development software on it. That's fine, odysseus has a certificate that proves he's learned about how great it is to have a process to solve these things, and is familiar with some enterprise software to set up tickets. So the developer just has to write a short essay justifying the business purpose of installing ripgrep. ...if he can find the process. Odysseus knows that a new developer isn't going to be given any hints about how to do any internal processes. In fairness to odysseus, he does appear to be the first one in the company to have conceived of the idea of writing company stuff down. so he puts a shortcut on the desktop to reach the IT help desk. And also the start menu. And also adds a bookmark to the browser. And also sets the browser's home page to be the help desk. And also has an always-on tray program that will connect you to the help desk. **and also does not allow any of this to be changed.** [odysseus] well I can't expect people to write down company.lan/help. they're not smart like me. Brian... who knows, these days. Cuts you a check to buy your own damn laptop? Saves on shipping, if nothing else. ## Development Sequencing: Entity Framework Migrations what's your favorite library? mine is Entity Framework. [some kind of romance montage, girl's face gets the EF logo (which is just a purple square with lowercase "ef", boo), guy's face is a burger] One of the things I love about Entity Framework is Entity Framework Migrations. [small sub-clip of previous romantic montage, but the "ef" is now "ef+m". if we can use that song "so happy together", specifically the part with "me and you", we can edit for this part to be like "me and you and you and you"] What that enables is that as your database schema evolves and is developed, you can control the versions with your version control software. Now, knowing this, here's a hypothetical scenario: There's an existing database that needs to be ported from an old version to a new version. someone has run some tool to generate entity framework models for... most of the database. Brian makes changes, and as he does, makes a migration, which he checks into version control. once tested, the migration is run on the production database. All of brian's coworkers can run a mySQL instance locally, and run the migrations to update it. They can possibly even collaborate on creating entity framework models. Ideally, brian also updates the migration seeds with realistic data. Odysseus, however... He's working on the database import as a singular JIRA ticket. He's imported the old test data into his local mySQL instance, and sends around a several GB file - the initial database dump. Meanwhile he's working on a "development database", which is hosted on a company server - all developers connect to that one, once it's up, while it's up. Oh but wait, the best part: he plans to initialize and then check in his singular Entity Framework Migration once the JIRA ticket is done. if only a coworker was allowed set him straight. ## Stay in your lane Why, one wonders, would entity framework migrations be technically used but not in a way that could be tracked in source control, or would enable collaboration? Why is the notion of planning met with another choice odysseus quote; [other] we don't need to plan anything, everything's been decided already The lesson should be clear: stay in your lane. Don't "fix" anything, these "issues" are intentional. It's like the organization is built on siloing. an earnest developer might carve out a silo of sanity for himself, and build up a sort of fiefdom. soulless business husks like this, they can understand it as a hegemony. If we in any way had the goal of building something larger than any one person can own, we must. share. responsibility. Odysseus does not and can not understand this. Because the product is barely incidental, the goal is the work. It's an absurd state to be in when an orginization's entire raison d'etre is to *extract* value from the customer, from society, from any system they're a part of. They are parasites.