[HN Gopher] Happiness is a freshly organized codebase ___________________________________________________________________ Happiness is a freshly organized codebase Author : felixrieseberg Score : 68 points Date : 2020-05-07 19:07 UTC (3 hours ago) (HTM) web link (slack.engineering) (TXT) w3m dump (slack.engineering) | dangwu wrote: | Interesting read! | | > No more dumping ground folders like "Helper" or "Utility" | | If you're organizing by feature and you have one of these | helper/utility classes that support multiple features, where does | it live? Would you consider each utility to be its own "feature"? | drawkbox wrote: | It depends on how many projects you have. A place like Slack | has only one client/product really when it comes down to it, so | they can get pedantic about it and look down on smart reuse | across projects with robust tested libs on top of standard libs | that are necessary to increase production and | stability/security etc. | | If you work at an agency, or game company shipping many games, | there will always be a "core" library or "base" library that | has Helpers and Utility. Good consistent projects wanted tested | and solid parts. Every game has a common lib of core tools like | maths, vector tools, prediction, data structures and many more. | | There is such a broad difference in coding on one platform for | one company for one product compared to shipping many companies | (or many internal projects) on many platforms for many | products. | | Even standard libraries for platforms are essentially a | Helper/Utility really when it comes down to it: .NET Core, | Python standard lib, standard node, C++ distributable etc. | There will be common core tasks across a project, product, | company or platform for most teams with many projects to | manage. | | If you don't have common libs with common helpers/utilities in | large sets of products, technical debt and maintenance become a | nightmare. In projects we work on these are core/base libs that | are submodules in git and are essentially the 'tech' across | projects where the project/product itself is the unique | implementation for that product. Anything common or generic | gets put in the core 'tech'. Every single game studio will have | these as well as agencies if they are organized and produce | quality relatively fast and consistent. | | If "Happiness is a freshly organized codebase" they must have | common tested parts that are ship tested. | | This is really just Slack talking specifically about mature | products at a company that only has one product. It isn't | reality at places that have many projects, products, companies, | clients etc. Pretending they are the same is a bit elitist. | ericaengle wrote: | We discovered a lot of cases would be unique, so we do try to | evaluate a "best" case when it comes to each move. | | Keeping that in mind, if a feature has a shared helper across | components within the /Feature folder, we'll have a /Shared | folder within to capture these files. At a top level, we would | encourage a new /Feature folder since most likely it will have | tests or be important enough to merit a folder rather than | squishing it into another /Feature folder. We're optimizing for | visibility too, so having it's own /Feature folder helps with | that. | dangwu wrote: | Thanks! And am I correct in assuming Slack just has a single | target for the iOS app (App), then one for each App | Extension? So the root source folders map 1:1 to build | targets? | ericaengle wrote: | Yes! that's the idea, or at least what we've worked to | achieve | brundolf wrote: | I actually think it's vitally important to have "dumping | ground" locations for things that aren't fully figured-out yet. | If I'm working on a new thing and I have something that's | relevant in multiple files but I'm not sure where it should go, | or even whether it will stick around, I don't want to have to | come to a screeching halt just to make a taxonomic decision | about something that's still a work in progress. The key, of | course, is going back and categorizing those things later once | you _do_ have an idea of where they should go. | ryanianian wrote: | > The key, of course, is going back and categorizing those | things later once you do have an idea of where they should | go. | | Yes, but in my experience this is rarely done. It's put off | until it gets so convoluted that it introduces bugs. The same | is true even if you do avoid dumping-grounds: you need to | examine the project's state _somewhat_ regularly to ensure it | still makes sense. Where code lives can be as important as | the code itself and thus deserves at least occasional review | and change. | allenu wrote: | Totally agree. So much of programming and designing is | figuring out what pattern your code is falling into and what | stuff "is" or "means". | | I believe it's totally okay to name things utilities when you | haven't yet established a common pattern or understanding of | it. Naming things is hard and spending so much time on it and | organizing things can often block you from progressing to a | place where you DO have more information and can | intelligently name things. | | You'll always be juggling unknowns, so it's okay to have | dumping grounds here and there, so long as over time you | clean them up as you gain more info. | brundolf wrote: | I recently started working within a Python codebase, and | one of the things I really like about it (not sure whether | this is standard Python practice) is that most directories | have a "common.py" file in them. So if you just want to put | something somewhere real quick, you can elevate it to | exactly the appropriate directory-level instead of going to | a single, global "utils" file. It's a neat pattern. | honkycat wrote: | I always argue adding "helper" or "util" adds nothing to the | description of a file/module/class and is better left off | nilkn wrote: | I feel like these names convey a lot, though. When I see | something like this, I immediately expect it to not do any of | the following: (1) contain core logic or define primary | classes/types/records; (2) have dramatic side effects; (3) be | hard to test; (4) reference or depend on other parts of the | codebase; (5) do anything controversial in general. | | I'd say I'd expect a "utility" to adhere to these conditions | more strongly than a "helper," which might be a bit more | entangled with application logic. | seph-reed wrote: | Utility - few or no dependencies. Absolutely no dependencies | specific to the project. (array flattener, 2-way map, etc) | | Helper - function which makes some really common project | specific code more DRY (createApiError, genCommonConfig, etc) | ravenstine wrote: | I'd also consider a Helper to be something that may have | application-awareness. Whereas, as you stated, a Utility | would have no serious dependencies and is otherwise "dumb". | [deleted] | rukittenme wrote: | I've been wondering a lot recently if folders make projects | better or worse. | | For example, when I write a library its usually very simple. | There's a single directory which contains all of the source | files. When people use the library they: | | """ | | import lib | | lib.run() | | """" | | Dead simple, no complex module paths to remember, no hierarchical | folder structure forcing you to code based on a pattern rather | than functionality. Pure bliss. | | But on the other hand, I have projects that contain 100k lines of | source code. I can't just leave it out in the cold. So poor baby | gets a couple of folders. | | But I do hate it. I hate writing the code. I hate reading it. I | hate finding it 6 months after the fact. | | That's probably just the nature of the job. It is work at the end | of the day. Maybe its just doomed to be hard. | osener wrote: | I'm not plugging the language, but I've come to appreciate | OCaml's module system with no imports and (mostly) globally | unique module names. No circular dependencies allowed either. | You can have multiple modules within a file (which is also a | module named after the file name). | | I structure larger projects as libraries with minimal | dependencies that depend on one another, and dump all my | modules with descriptive file names under the same directory | within the library. | | I vaguely remember reading something that hinted at Facebook | doing something similar with their React components. | ldd wrote: | There is this wonderful utility: dependency-cruiser[0] for | javascript / typescript projects. | | It visualizes dependencies in a project. I found it so so easy to | refactor and move files around after I started using it. I am not | usually a visually-oriented person, but for this usecase, and to | be happy, `dep-cruiser` surely helps. | | [0]: https://github.com/sverweij/dependency-cruiser | kccqzy wrote: | I sometimes wish Git properly records copy and move information. | It turns out Git's heuristics for detecting copies and moves work | about 95% of the time, and for the remaining 5% it's mildly | annoying to read a git blame with every line from a move. You can | blame further across that move, but that's manual. If copy and | move information is perfectly recorded, I'd have more incentive | to do these kinds of code reorganization. | ryanianian wrote: | This article mentioned a while ago had relevant info: | https://news.ycombinator.com/item?id=22689301 | | In my experience, ensure you don't change the code as you move | it, don't rename files and move content at the same time, and | don't move "too much" code in a single commit. Around 2k lines | at a time seems to be a good number. Maybe some | languages/structures are easier for git to analyze when doing | blames. | kccqzy wrote: | How do you not change the code as it's being moved, when you | need to update #include directives, imports, and other file | paths? | ryanianian wrote: | Sometimes it is unavoidable, but in general minimize it. | Don't refactor a class and rename it at the same time. | Hopefully moves just result in imports and paths changing, | something that is not likely to confuse a blame (and rarely | do I care about the blame lines on #includes/imports) | derefr wrote: | It's easy if not every commit needs to compile. :) | rudi-c wrote: | I've encountered similar difficulties around codebases with a | lack of file hierarchy structure. But one major difficulty in | fixing the issue is that moving a lot of files around tends to | trash `git blame`, which is often more valuable than knowing what | folder to put a new file in. Is that something you've | encountered? | smallnamespace wrote: | Try putting the refactoring commit in .git-blame-ignore-revs | Lammy wrote: | Use Mercurial? :) | ryanianian wrote: | There are workarounds to get git to search harder or to commit | things in a way that's helpful for large-scale file-shuffles, | but to be honest I've rarely found this to be enough of a | reason to not move things around. The end result is a much more | productive and purposeful place for code to live and grow. This | said, moving files and moving code within those files at the | same time is a recipe for confusing yourself and git. Move | files first, then content. | [deleted] | ryanianian wrote: | The concept of linting repo structure is an excellent one. | | I cannot even get started in projects without a sane repo layout. | Source files scattered everywhere unrelated to each other, utils | junk-drawers with dozens of files, multiple top-level source | directories without explicit rationale, tests and source totally | disjoint, hacks to modify build and run paths, implicit | dependencies between directories, awful convoluted build-system | configuration to match all of these idiosyncrasies, and | impossible or very difficult editor/ide integration as a result. | Even rails-eque apps (which come with a reasonable structure) get | really messy really quickly if somebody doesn't have the | diligence to stay on top of it. | | I want there to be a {sane, polyglot, large-ish-scale} project | layout convention that plays well with | {intuition/discoverability, build-systems, editors, project | size}, but it seems there's an inherent tension between these. | Maven tried. Kinda. I wonder if there's a fundamental problem we | aren't solving somewhere. | DelightOne wrote: | In maven I don't like the test-separation from the code itself | because its much simpler to find and update them this way. | ryanianian wrote: | There's a lot to not like about maven. I think that | separation was motivated by java's classpath rather than | making a 'good'/dev-friendly convention. After 10 years in | maven-land, I changed a recent project to put `foo.ts` and | `foo.test.ts` in the same directory and its wonderful, but it | confuses some build-systems and makes certain kinds of test- | helpers harder to write. | bob1029 wrote: | I feel like a lot of the pain in codebase organization boils down | to having a technical project structure (i.e. layout on disk) | that does not align well with the business. Obviously, it's | impossible to force something to directly align with such | disparate and abstract requirements, so you have to create | abstractions (layers) to enable a hypothetically-pure realm. | | In our architecture, we've created roughly 2 different kinds of | abstraction: Platform and Business. | | The Platform abstractions are only intended to support themselves | and the Business abstractions. These are not supposed to be | easily-accessible by non-technical folks. Their entire purpose is | to make the Business abstractions as elegant as possible. The | idea is these should be slowly-changing and stable across all use | cases. Developers are only expected to visit this realm maybe | once every other week. We effectively treat ourselves as our own | customer with this layer. | | The Business abstractions are built using only first-party | primitives and our Platform layer. Most of this is very clean | because we explicitly force all non-business concerns out into | the Platform layer. Things like validation logic are written | using purely functional code in this realm, which makes it very | easy to reason with. Developers are expected to live in this area | most of the time. Organization of business abstractions typically | falls along lines of usage or logical business activity. We | explicitly duplicate some code in order to maintain very clean | separation of differing business activities. I've watched first | hand as the subtle variances of a single combined Customer model | across hundreds of contexts eventually turned it into a boat | anchor. | | As a consequence of all this, our project managers and other non- | code-experts are actually able to review large parts of the | codebase and derive meaningful insights without a developer | babysitting them. We are at a point where project managers can | open PRs for adjusting basic validation and parameter items in | our customers' biz code. Developers also enjoy not having to do | massive altitude shifts on abstractions while reading through 1 | source file. You either spend a day working platform concerns, or | you are in the business layer. Context switching sucks and we | built to avoid it as much as possible. IMO, having a bad project | structure is one direct side-effect of being forced to context | switch all the time. | ajsharma wrote: | Wow, I love this idea. I'm curious how you have that structured | within the code base. Is it literally a platform folder and a | business folder? ___________________________________________________________________ (page generated 2020-05-07 23:00 UTC)