[HN Gopher] Code colocation is king
___________________________________________________________________
Code colocation is king
Author : vnglst
Score : 154 points
Date : 2022-02-01 17:51 UTC (2 days ago)
(HTM) web link (koenvangilst.nl)
(TXT) w3m dump (koenvangilst.nl)
| slaymaker1907 wrote:
| I think this is a pretty good rule of thumb. Does anyone know of
| any metrics to see how an individual codebase does in this regard
| by looking at PRs/commits?
|
| It is largely because of this reason that I disdain patterns
| which separate things out into top level folders like /views,
| /reducers, /actions, /utils. Most code should be organized at the
| feature level with separate modules for global utilities (which
| are always hard to organize).
|
| I think this methodology works well because more files means more
| mental overhead and context switching, particularly for code
| reviews. The best code review given a reasonably sized change
| (not 20kloc) is a single file where the file contains little not
| relevant for the changed code. Not only is it easier to
| comprehend, it also avoids unnecessary merge conflicts.
| The_rationalist wrote:
| NTARelix wrote:
| I've found that code colocation is great, especially combined
| with a directory structure that mimics the application's
| hierarchy/layers. At my place is employment we try to follow a
| principle of "keep related code contained to a single place". On
| its own this ended up causing some problems with "reinventing the
| wheel" and a relatively inconsistent experience when crossing
| borders between team ownership. The big thing that has helped a
| ton is having a regular meeting with nearly all employees working
| on the same part of the stack. We used the meeting as a space for
| talking about what we're working on and even get into the details
| of "I'm solving problem X by building solution Y." At which point
| others can chime in with interest in using it for their own
| upcoming projects, describe how they already have a solution to
| problem X, or provide suggestions. It's not the only thing
| discussed in this meeting, but it has been important for
| improving the consistency and general quality of the product. The
| resulting code follows the same principle above, but reused code
| ends up bubbling up to a "common" space in the lowest common
| shared layer; whether it's a new directory or a new package used
| by multiple projects.
|
| A couple new problems we're dealing with now:
|
| 1. Finding older common things and deprecating them. When it's in
| a common space it feels like it's everyone's responsibility which
| means nobody ends up working on it. Maybe more narrow, clear
| ownership would solve that problem.
|
| 2. Someone finds the common thing that almost fits their need, if
| only it had one more little feature. The problem is when this
| happens many times and you end up with this complicated beast of
| an abstraction. This is probably solved by finding ways to
| decompose the abstraction and by following a principle of "do one
| thing well" or something about simplicity.
| jbreckmckye wrote:
| Counterpoint: for a long time, the convention was to store test
| files in a structure that 'mirrored' the source files. I never
| had a problem with this, and personally I don't know why it fell
| out of favour.
| tpoacher wrote:
| Unfortunately, 'code that goes together' is rarely "all these 3
| things go together", but more "a and b goes together in one way,
| b and c in another, and a and c in another. Meanwhile, c is more
| naturally contextualised as d, which has nothing to do with a or
| b, so it should really be close to d and far away from a and b,
| but somehow still close" etc etc ad nauseam.
|
| So, really, the only real way to keep functions linked is to not
| be lazy, and try to have proper documentation locally in your
| function which includes a "see also" bit.
| yakshaving_jgt wrote:
| Just stick everything in the same file until it actually hurts.
| This presentation[0] gets it right.
|
| [0]: https://www.youtube.com/watch?v=XpDsk374LDE
| arvindrajnaidu wrote:
| I agree. Tests for example used to be in a separate folder
| mirroring the code folder structure. I have moved away from that
| to put it right next to the file.
|
| Random thought: Can the IDE recommend next file to work on -
| "people who worked with this file, also worked on". The real
| problem is not where we keep it, it's how quickly we can get to
| it.
| jbreckmckye wrote:
| You see, this is a design that's taken hold of a lot of
| codebases, and I don't think it works. I see a lot of projects
| with big folders mixing top level exports, implementation
| files, tests, test utils, fixtures and the like.
|
| I'd much rather see the 'mirrored' approach.
|
| > Random thought: Can the IDE recommend next file to work on
|
| I'm _sure_ I've seen something like this in IntelliJ.
|
| EDIT: I did! It's in the changes tab > the eye icon > 'show
| files related to active changelist'. It then sometimes will
| make suggestions based on project history.
| nerdponx wrote:
| Tests that involve complicated setup or are otherwise bigger
| than "unit" tests probably don't make sense to keep in the
| source tree.
|
| But it's a nice idea to keep unit tests specifically alongside
| the "units" of code that they test.
| larsrc wrote:
| Modern IDEs have made this less important than it used to be, but
| it still matters. My quick rule of thumb would be to follow the
| mental model of your code. Things that you need to read to
| understand a given aspect of the code should be close together.
| hyperman1 wrote:
| That's just the small scale version of it. In a large
| organization this gets a lot more important. If you have
| multiple applications, and each needs roles like database ,
| developer, architect, ... then there is a push to move all
| database management to a centralized DBA team, all devs to a
| dev team, all architecture to the architects team.
|
| One thing that gets lost is the application-specific knowledge,
| which is the whole reason you are doing things. Yes, your
| database is now near-optimally managed according to $vendor,
| except it's a poor fit for the application, as it received a
| one size fits all configuration.
|
| Second problem is velocity. If 1 team can adapt the database
| structure, modify the backend code and services, you'l go a lot
| faster than if you have to book a databaser for a week, a
| developer for 2 weeks, etc...
|
| Next problem is implicit waterfall: The developer will hide
| data in the wrong columns, because otherwise the databaser has
| to be called back, which causes rescheduling and a rebudget(
| i.e. management now hates you). It's only temporary, everybody
| tells themselves, until the right person revisits the
| application again next month.
|
| And god help you if the architect did not deliver perfect work
| the first time around, because then everybody is creating
| things that don't fit together. The architect being of course
| the guy/girl who drew some boxes and arrows between 2 very
| important meetings, at the point where business requirements
| were not yet delivered and nobody knew what the application
| actually needed to do. Architects are expensive, so they're
| long gone before the first character of the code is ever
| written.
|
| Final problem: Your application is now spread over 10s of
| silos, and nobody knows what connects to what. Any maintenance
| done on it starts with someone walking around between silos,
| asking everybody what their piece of the puzzel is.
|
| So my opinion is to do the reverse: Let the dev team modify the
| database structure, even if the result is clearly suboptimal.
| They will feel the pain from their mistakes and have the
| ability to fix things. It might take a while, but a coherent
| team will figure things out. A loose bunch specialists,
| available part-time? Not a chance.
| chrisweekly wrote:
| You make some very good points (echoing Conway's Law:
| software will reflect the org chart). Smart companies are
| constantly seeking better ways to structure teams and
| processes to mitigate the downsides at either extreme
| (horizontal layers : vertical stacks).
|
| IMHO, over-emphasis on efficiency is a common trap;
| efficiency and agility are at the poles, and it's the latter
| that often matters more.
|
| So, how to strike a pragmatic balance? I've been part of
| successful experiments with a small, free-roaming,
| multidisciplinary "red team" or "green team" that crossed org
| boundaries to solve gnarly problems, free up log jams, and
| facilitate step-function improvements in standard teams'
| capabilities.
|
| > "Architects are expensive, so they're long gone before the
| first character of the code is ever written."
|
| As a hands-on architect, I don't consider my work complete
| until there's been meaningful collaboration with dev leads
| and in-depth review of working code. It's a dynamic and
| iterative process. Immutable, ivory tower boxes-and-arrows --
| divorced from the realities of actual software development at
| any kind of scale -- are insufficient. A prescribed, linear,
| waterfall process of biz req -> architecture ->
| implementation is bound to fail. Success requires embracing
| the rich interplay between various forms of software design
| (requirements, IX/UX, architecture, and implementation) and
| stepping in and out of them where and when appropriate.
| bin_bash wrote:
| I feel this practice makes code review a lot easier, not just
| authoring.
| jrochkind1 wrote:
| This makes sense to me.
|
| And it also seems notable to me that Rails (which I generally
| like; I am not a Rails hater this is not a Rails hate comment) --
| often seems to be trying to do the opposite. Like separating a
| controller and it's view code -- things which in the typical
| Rails app are pretty tightly coupled -- into the higher level
| controllers and views folders, putting all controllers next to
| each other and all views next to each other, instead of a
| controller next to it's coupled view. (which I do find an
| inconvenience as a developer).
|
| But the risingly popular view_component library for use with
| Rails makes the 'proximate' choice, putting the view template(s)
| next to the logic it's coupled to.
| rubyist5eva wrote:
| as a rails dev, I agree with this, instead of
| app->{models/views/controllers}->resource, I would prefer
| app->resource->{models/views/controllers}
| jrochkind1 wrote:
| Interesting, because I realize I would not personally want
| that done with models, in my apps models/controllers
| frequently don't have a 1-1 relationship.
|
| But controllers and view templates almost always do, because
| of the nature of the architecture.
|
| So actually just using the `view_component` gem for all my
| views from now on (not just partials) probably satisfies me!
| Ixio wrote:
| This is kind of what Django does instead and I much prefer
| the Rails way. Though it might just be because I started MVC-
| dev with Rails.
|
| I think I find it easier to find common abstractions on the
| {models/views/controllers} level than the resource level and
| that frustrates me with Django app-based dev.
| kevinwang wrote:
| > One of the things I struggled with when I started out as a
| programmer was where to put my code. It was not something I could
| easily find in tutorials and for a long time I wondered why
| everyone was so focused on how to get framework X to do Y, when
| all I wanted to know was where to put the code that does Y.
|
| So true. Small things like this are why I'm so glad I had a
| software engineering job at a real company for a few years, even
| though I don't necessarily want to do that for life. I learned a
| lot from looking at codebase conventions and quick questions to
| the elders. Unfortunately, no one can help you with the other
| hard problem: naming.
| ehnto wrote:
| That is one reason to choose a framework instead of building
| from scratch, so you don't have to make decisions like "Where
| do I put Y". The most import value a framework provides isn't
| in the way it functions, but that it is a collection of idioms,
| decisions and conventions that everyone has to use.
|
| It also means I don't have to worry about where Dev X put thing
| Y, because I know where to look for it already.
|
| That's what software patterns are good for as well, a shared
| set of idioms so you don't have to invent new approaches. I
| think that's important in a professional environment but less
| so in a personal one. For personal projects I just do whatever
| is fun.
| zzbzq wrote:
| But the whole problem is frameworks always decide against
| code colocation, and also they don't tell you what to do with
| all the functions that don't fit their directory scheme.
| People who don't use a framework will naturally tend toward
| fewer subdirectories and code colocation just because, e.g.,
| there's no reason to elevate the importance of a
| models/views/controllers pattern.
| MauranKilom wrote:
| > Unfortunately, no one can help you with the other hard
| problem: naming.
|
| I have a guiding principle here: If, a few minutes after naming
| something, you want to use the thing and (without looking it
| up) your first attempt at writing the name is correct, it's a
| decent name.
|
| If you name a function _processFoos_ and then later in your
| code your first intuition for what it was called is
| _createBars_ , then there is some disconnect (and possibly some
| missing conceptual clarity) in what this function is supposed
| to accomplish. Is it more about accepting _foo_ s or is it more
| about creating _bar_ s? I find it very productive to, in such a
| case, dive a bit deeper into these differences (which may often
| also reveal something about different perspectives between
| caller and callee).
|
| So TL;DR: If you don't have to look up the name of what you
| wrote minutes ago, it's probably a good name.
| k__ wrote:
| I think, colocation is even more crucial than most coding purists
| think.
|
| CSS-in-JS and Tailwind greatly helped here to encapsulate things.
| rcthompson wrote:
| I wonder if you could apply this principle by writing a tool that
| goes through your git history and identifies distant regions of
| code that are often changed at the same time, so you can take
| some action to link those regions. At a minimum, adding "see
| also" cross-references, if not refactoring to bring them closer.
| mjul wrote:
| This is one of the key concepts in how Codescene analyses a
| code base by mining the Git repo.
|
| It is quite useful to find find problem areas and tacit
| knowledge such as "when you change this API these two client
| side adapters should probably also be updated"
|
| See https://codescene.com/
| slx26 wrote:
| There are many different criteria to decide where to put your
| code or how to group it. And each case benefits more from one
| idea or another (and each codebase contains many different
| cases). Humans don't organize knowledge in folders in their
| brains. The information network is much more complex. We can't
| find a good solution only with folders.
|
| In fact, the idea of trying to model complex systems in a text
| format divided in files (most programming languages) doesn't
| quite hold... gracefully at least. For example, the frequent
| discussions about inheritance and generics are pretty revealing
| of the fact that we mix modelling and implementations in the same
| working space, when in fact in many cases it would be better to
| work on those at different layers.
|
| So, in my opinion, to really make "code colocation" better you
| would kinda need to start modelling complex systems with richer
| toolsets that don't try to express them only with code files. You
| can't properly work with complex systems with a single view, no
| matter which one you pick.
| ChrisMarshallNY wrote:
| I tend to rely heavily on the storyboard, in my Apple
| development. It's going to be going away, with SwiftUI, and I'm
| not convinced that what SwiftUI provides will actually be a
| replacement, but we'll see how things shake out.
| valenterry wrote:
| Totally agree. Files&Folders are almost omnipresent, which is
| why people get used to it so much that it starts to shape how
| they think about structuring things.
|
| But if you take a step back, they are actually not the right
| abstraction for most things. In this case, a tagging system
| would serve the purpose much better.
|
| Unfortunately, tooling that deals with files&folders and text
| is very mature and it will be hard to extend or even replace
| it.
| eezing wrote:
| No: ./User/index.ts ./User/interfaces.ts
|
| Yes: ./User.ts
| revskill wrote:
| Follow composition.
|
| If A calls B, then A folder should contain B folder.
| ReleaseCandidat wrote:
| And if A is recursive(ly calling itself)?
|
| :-D
| Liru wrote:
| cd a ln -s . a
|
| Problem solved; perfect organization.
| remram wrote:
| I tried this and disappointingly Linux rejects it before
| programming languages break.
|
| gcc: a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/
| a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/test.h:1:10:
| fatal error: a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/
| a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/test.h: Too many levels
| of symbolic links
|
| python: ModuleNotFoundError: No module
| named 'tmp.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.
| a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a'
| izoow wrote:
| I often thought about a concept of an "IDE" where there are no
| files. You would instead have a "database" of functions/classes
| that you work with. Some modern IDEs come close, but you still
| have to worry about files. I guess getting rid of files
| completely might require some extra magic from the IDE to make it
| work with current programming languages. Does something like this
| exist?
| 0xCMP wrote:
| Yep I think this will be my next major project. Taking
| inspiration from Rust, Go/Zig, Nix, Unison, Old SmallTalk and
| Self videos, Eve, and probably SQLite/Postgres.
|
| There is a lot of separate innovation that could be combined;
| so I think.
|
| While inspired from these various things I doubt it would just
| be "another smalltalk" or "just a nocode thing" which many
| modern versions of this end up looking like.
| pitaj wrote:
| You could technically do this with Rust. rust-analyzer will
| allow you to jump to certain types, functions, etc, and Rust
| has first-class modules.
| 0xCMP wrote:
| Things like rust-analyzer are exactly what I think lead me to
| believe... what if code was designed to be imported by a
| "database" and then changes could be made there and
| serialized back. Not too dissimilar to how unison lets you
| select functions to edit, but drop the cli and text files
| requirements. Create an API/UI/etc. to edit the "code" in the
| "database".
| michael_chip wrote:
| This sounds very similar to how a Smalltalk development
| environment works. Instead of a file system you have a class
| browser that you use to navigate your code.
| zzbzq wrote:
| In an abstract way it's not really different. Next you'll want
| namespaces and sub-namespaces, and we're back to files and
| directories, just built again on top of something that's not a
| file system.
| sixstringtheory wrote:
| I have an idea for an IDE that stores functions independently,
| and instead of being contained in classes/modules defined by
| syntactical scope delineated by eg curly braces, they simply
| get tags that you can browse/search/filter in various ways. I
| image something like a UML class browser with little planes
| containing code snippets and connections between them for call
| graphs etc.
| travisjungroth wrote:
| Take it a step further and instead of a database use content
| addressing. https://www.unisonweb.org/
| ehnto wrote:
| Agreed, it helps reduce the cognitive load and context switching
| by a decent margin. If you can't achieve co-location, at least
| structure the directories the same way or name related files the
| same thing.
|
| If you have a partial/catalog/product/buy.js file, have a
| partial/catalog/product/buy.scss file as well. Finding the CSS
| for the buy.js component inside add-to-cart.scss is very lame.
| awinter-py wrote:
| uhhh tricky
|
| if your codebase is 3 features each of which has 3 similar
| functions (listener, db model, middleware, let's say)
|
| do you group by similar functions? (middlewares.py, models.py)?
| or do you group by feature? (feature1.py). depending on whether
| 'work' is a feature change or a refactor this week, the
| correlations will be different
| kerblang wrote:
| A lot of programmers want to divide logic into Categories of
| Behavior and then package them up into behavior packages, so
| that you have to hopscotch all over the directory tree to
| reconstruct and navigate an actual _process_ from beginning to
| end; then they want to chop it all up into microservices in an
| attempt to compensate for the resulting mess.
|
| If you package logic according to the _process_ , it's a lot
| easier to control the scope of logic that is exclusive to a
| given process (some things are of course shared). Languages
| that can enforce package privacy, file privacy, etc. help a lot
| here.
|
| In general, unlimited scope & dependencies is what strangles a
| dev team to death, so any option that restricts scope to what
| is _necessary_ is arguably best.
| awinter-py wrote:
| by process, you mean call stack or series of event handoffs?
|
| I think you're right that that's one of the key ways that
| devs (esp noobs to the codebase) need to navigate, and must
| be supported. I think 'stack graphs' are supposed to do
| that[1], but have never tried them.
|
| but there are other navigation modes we need to support as
| well -- like if you're deleting something, or upgrading
| something, 'find all references'. if you're inventorying use
| of a DB type, you might want to audit field types in all
| models.
|
| I sometimes wonder if the future is tagging code as
| 'belonging to a feature', but they can live wherever --
| because devs have different needs on different days.
|
| [1] https://dcreager.net/talks/2021-strange-loop/
| kerblang wrote:
| By process I typically mean "business process", as in: Bob
| wants to ship all the widgets to the South Warehouse, and
| this pile of logic right here is going to do that for him,
| end-to-end (or mostly end-to-end).
|
| The best option I've found for "find everything that
| references this database table and see if it will be
| affected by this thing I need to change" is to give
| database tables really unique names (like "tbl_user"
| instead of "user") and just grep the hell out of
| everything.
|
| Also if I'm writing large programs I'm going to use a
| compiler. I mean if a linter can tell you "Hey this python
| won't work because you removed this function" then good for
| python, otherwise bad for python. And if I'm going to
| include 100 open-source libraries then god help me if my
| build system can't tell that one of them went missing or
| that the author removed a function I'm using in version
| 1.2.3.4.b.
|
| But all of that is about dependencies, and dependencies
| kill teams. That's why I want the scope of any logic as
| tightly constrained and enforced as possible.
|
| I'm sure you could design a multi-dimensional behavioral
| categorizer/IDE/language system that blows everybody's
| minds and wouldn't that be cool, but I doubt anyone would
| use it, because behavioral categorization really doesn't
| solve problems. It just looks nice.
|
| But any rate, at the very minimum, back to this point:
| Don't try to compensate for the failure of behavioral
| categorization with microservices when you could have just
| packaged things along the same lines of division.
| awinter-py wrote:
| 100% with you on greppability as an underrated design
| skill
|
| interested in 'library versions as types' and have been
| thinking about this problem on and off. version numbers
| are a proxy for the _combination_ of: 1) call signature,
| and 2) internal semantics
|
| linters / typesystems are good at (1). If we had a system
| that could do (2), version numbers would be less
| necessary and compatibility could be proof-based. (Well,
| to the extent that the semantic assertions are valid).
| ChrisMarshallNY wrote:
| _> keep the code that changes together close together._
|
| Agreed. I do that, but never got around to factoring out a one-
| liner that expresses it.
|
| I also tend to have fairly deep directory trees that tend to
| reflect the code hierarchy/structure.
|
| I will also factor out large chunks into standalone subprojects,
| and reinclude them as packages. This results in _very_ high
| quality code, and also gives me dozens of libraries of tested,
| ship-quality, reusable code.
|
| Another thing that I do, is have fairly large source files, that
| aggregate multiple classes/structs/enums/protocols that relate to
| each other. It drives me nuts to deal with the typical Java "Each
| class has its own file -no matter how small" thing.
|
| I practice what might be called "eXtreme documentation. I rely on
| Jazzy (sort of like Doxygen), and now, DocC, so every element in
| my code has a headerdoc. I make heavy use of the "// MARK:"
| macro, as well.
|
| I write about that, here:
| https://littlegreenviper.com/miscellany/leaving-a-legacy/ (I need
| to add a DocC update).
| radicalbyte wrote:
| > Another thing that I do, is have fairly large source files,
| that aggregate multiple classes/structs/enums/protocols that
| relate to each other. It drives me nuts to deal with the
| typical Java "Each class has its own file -no matter how small"
| thing.
|
| This can go both ways - I've seen plenty of projects where you
| have to dig through 3k lines of unrelated spaghetti code just
| to find the bit you need. That turns into a special piece of
| hell when you have multiple team members working on it.
|
| The original motivation for having more small files was driven
| by these "god objects" and the limitations of version control.
| CVS, Visual Source Safe and to a lesser extent Subversion were
| much easier to use together with smaller files.
|
| If you're using modern version control (git) then that reason
| has gone.
|
| My personal preference is to split horizontal concerns into
| their own modules and depend on them; they become the core /
| support libraries for my team/organisation. For services I try
| to keep the domain-specific parts close. Tests go in a
| different module which mirrors the structure. When units start
| to get big (or become cross-cutting) they get factored into
| smaller units or other modules.
|
| In the end it's all a balance and unless you're under outside
| constraints ( _cough_ SonarCube box-ticking busywork idiocy
| _cough_ ) then you can generally keep it sane.
| whoisthemachine wrote:
| > It drives me nuts to deal with the typical Java "Each class
| has its own file -no matter how small" thing.
|
| There is value in keeping some source together, but I think
| it's more judgment based than you imply, it can also drive me
| nuts when I have to dig through a dozen 2k+ line files to find
| the thing I need to change, and it also reduces my confidence
| that I can change that thing without introducing unwanted side
| effects (unit tests help increase that confidence regardless of
| code structure).
| ChrisMarshallNY wrote:
| Fair point.
|
| As someone that regularly digs through 200-1.5K files (2K is
| a bit much, for me), I can report that good documentation
| makes a _huge_ difference.
| whoisthemachine wrote:
| Good documentation is definitely helpful, but expensive and
| challenging to maintain (and keep correct). In my personal
| experience, documentation is out of date or invalid nearly
| the minute the ink dries... much better (again, in my
| experience) to have good, descriptive unit tests that fail
| when the code changes.
| ChrisMarshallNY wrote:
| Unit tests are not always a good match with the
| application:
| https://littlegreenviper.com/miscellany/testing-harness-
| vs-u...
|
| I tend to use a lot of test harnesses (which, IMNSHO, are
| _much_ better application exemplars than most unit
| tests).
|
| I also use a lot of headerdoc stuff. In Xcode, it allows
| your own code to show up in the QuickHelp navigator tab,
| and you can generate really good SDK docs.
|
| It also stays fresh. Easy to maintain. Using "breakers"
| is a huge aspect of my code. Makes it much easier to
| scan, and liberal use of // MARK: is good.
| ryanar wrote:
| I followed the same pattern when I had the time to. Instead of
| building the thing, build it as a separate package and include
| it as a dep. Packaging code up for open source consumption
| leads to better docs, tests, and tighter abstractions that are
| generalized.
| janto wrote:
| I think that's the opposite of what the parent says: first
| build the thing and only then factor it out and reinclude it.
| ChrisMarshallNY wrote:
| Not completely. The way that it works for me, is that I
| start work on a project, and, while building, I notice that
| some code that I'm working on is:
|
| 1) Pretty complex, and fairly insular; and/or
|
| 2) Possibly useful, elsewhere.
|
| If that's the case, I will then stop work on the main
| project, and take some time to extract and "genericize" the
| subproject. I'll usually set it up as a standalone open-
| source project; complete with tests and documentation. As
| the commenter stated, I think that this results in some
| excellent code. I always clean the house before the guests
| arrive.
|
| This may happen before I have completed the coding in the
| main project, or may happen as the result of a review,
| after the fact.
|
| In some cases, I very clearly need to develop a subproject
| before starting on the main project, or before certain
| milestones within that project (for example, SDKs or
| drivers). In that case, the timelines are completely
| separate.
|
| If you look at my GH repos, you'll see a whole bunch of
| these projects, including some rather strange ones, like an
| XML duration parser[0]. These are the types of projects
| that I extract.
|
| In some cases, I end up not using the extracted project in
| my main project (happens to some of my UI widgets). In that
| case, even though I am not using it, I still have an
| excellent project for the future. Here's an example[1]. I
| have ended up not using the spinner in my own work, as it
| was too obtrusive a widget, but it's nice to have it
| available for future projects.
|
| [0]
| https://github.com/RiftValleySoftware/RVS_ParseXMLDuration
|
| [1] https://github.com/RiftValleySoftware/RVS_Spinner
| corpMaverick wrote:
| This sounds a lot better than the Single Responsibility
| Principle that is often quoted and IMHO often misunderstood.
| camtarn wrote:
| > It drives me nuts to deal with the typical Java "Each class
| has its own file -no matter how small" thing.
|
| Couple this with extensive use of inheritance and design
| patterns, and you have a recipe for awfulness. One of my
| previous teams had an implementation where rendering a piece of
| HTML would involve digging through about twenty different
| source files, each with maybe 3-4 lines of actual code other
| than the class definition boilerplate. One top-level line would
| send you down the inheritance hierarchy of ClassThatGetsData/Cl
| assThatGetsDataPlusThisOneOtherThing/...PlusThisOtherOtherThing
| /... etc etc, then the same thing for
| ClassThatProcessesData/... and then ClassThatRendersData/...
|
| Adding a single bit of data to rendered HTML (e.g. a tiny star
| for 'this product is well-reviewed') would involve altering
| every single source file in the multiple trees, and maybe
| adding some new specialisations to make the trees even deeper.
|
| At the time, fresh out of Uni with a head full of design
| patterns, I thought this was just how enterprise code was meant
| to be structured!
| ravenstine wrote:
| > At the time, fresh out of Uni with a head full of design
| patterns, I thought this was just how enterprise code was
| meant to be structured!
|
| Ugh... and that's the flaw in teaching design patterns.
|
| Don't worry, that happened to me too, though I didn't go to a
| university.
|
| Design patterns are an advanced tool. If you're learning to
| code, design patterns are mostly non-applicable to the kinds
| of problems that newbs are solving. Design patterns are meant
| for specific problems, but somehow we end up believing
| they're the end-all-be-all and that we must be using _some_
| design pattern in our work. If you have design patterns
| pounded into your head, and you believe the only design
| patterns that exist are the one 's someone has already named,
| then not using a particular design pattern can seem like
| chaos even when it's not.
|
| I wish we'd stop teaching that shit. If you've been coding
| long enough and faced complex enough problems, you'll either
| come to embrace some design patterns or you won't. Otherwise
| they'll likely just be misapplied.
|
| OOP fits this view of mine as well. In general, I don't think
| most programmers need to be aware off OOP principles because
| they will almost certainly misuse them. And to what end?
| Several single purpose classes and "tiny functions" scattered
| across multiple files that others are now forced to jump
| between.
| Too wrote:
| Most "design patterns" are actually just workarounds for
| lack of expressiveness in old Java versions.
|
| In other languages or modern Java you can replace half of
| them with simple constructs. Factory pattern for example
| can be replaced with simple inline callbacks to anonymous
| functions. Same with many others.
| novembermike wrote:
| It feels like half the usage of inheritance is also just
| workarounds for mocking things in unit tests or being able to
| access things that are private/protected. Everything ends up
| with an interface and an impl just because of silly language
| decisions.
| radicalbyte wrote:
| It takes a long time for some people to understand that
| complexity is the enemy; and for patterns in particular it
| takes some people a long time to learn _when_ to apply them.
|
| Same thing for algorithms: BloomFilters are the classic
| example. You can tell when it has hit the font page of
| HackerNews recently because suddenly everyone starts looking
| for problems that it can solve (poorly).
|
| Just don't get me started on blockchain... :-)
| misterpurple45 wrote:
| I was with this post until the author mentioned a /utils or
| /helpers directory, which made me sad.
|
| No piece of code is so bland that it can't be placed somewhere
| with a name that describes what it does or what category it
| belongs to.
| danielovichdk wrote:
| I second this. It's poor naming and should be avoided.
| Tarucho wrote:
| "Code colocation"?
|
| >Turns out that "where to put code" is one of the hard things in
| software engineering and there are no silver bullets. That's part
| of the reason why there are so few easy tutorials on this
| subject.
|
| This is called modularization and there are lots of papers and
| books that discuss it in decent detail.
| eatonphil wrote:
| In Python it's especially scary to have some resource management
| at the end of a block. I have made bugs so many times when after
| refactoring I screwed up the indentation and the resource
| management at the end no longer happened in the right way.
|
| The pragmatic approach I've taken to deal with this is make
| heavier use of custom context managers so creation and cleanup
| code must stay together.
|
| But I'm also getting tired of whitespace sensitive languages.
| marcosdumay wrote:
| Python whitespace bites me every once in a while. But this does
| not generalize to other languages, in particular, I prefer
| writing Haskell with whitespace sensitivity1 and I don't
| remember it ever being a problem.
|
| 1 - Last time I asked "who doesn't" on the internet, I found
| somebody who doesn't. But it's by far the most popular option.
| eatonphil wrote:
| True. Languages with static type checking probably have this
| easier because you'll have some warnings about differences in
| scopes or unused variables.
| elcritch wrote:
| Similar benefit in Nim where types help catch stray indent
| issues. Plus proper scoping of automatic variables in 'for'
| loops helps too.
| marcosdumay wrote:
| It's not that.
|
| The whitespace works differently between those languages.
| The Python syntax has limitations that aren't on the
| Haskell one.
|
| But also, Haskell tends to place the kind of things the GP
| is talking about on the beginning of your expression
| (because of the "reverse" order on function composition),
| while Python tends to place them at the end of a block.
| VBprogrammer wrote:
| It's funny, I've been a professional python dev for 8 years or
| so and I can't remember a single instance of whitespace having
| caused a bug.
|
| The closest I can think of is: if something:
| ...handle something.. elif something_else:
| ...handle something else.. if other_thing:
| ...handle other thing... elif yet_another_thing:
| ...handle yet another thing... else: ...for
| everything else...
|
| If I had a frustration with python it would be the prevalence
| of "who needs types when we have dicts". This situation has
| been somewhat improved by dataclasses but lots of our code
| predates them by many years.
| w_t_payne wrote:
| It's a bit out of date now, but here are some of my thoughts on
| this from 10 years ago:-
|
| http://williamtpayne.blogspot.com/2012/07/structure.html
|
| http://williamtpayne.blogspot.com/2012/05/development-concer...
|
| Looking back on this now, it all feels a bit unconventional and
| strange, so I wouldn't make the same recommendations today. I
| think that people want to operate in an environment with fewer,
| and less rigid constraints. I _do_ think some of the driving
| concerns are still valid though, and still worth considering.
| prettyStandard wrote:
| It's also good for caching/performance reasons.
|
| I don't know too much about compilers, but if you're improved
| proximity results in better proximity of the byte code then
| you're less likely to have a cache miss. That's a big deal.
| tagh wrote:
| Makes perfect sense! This would help lead to low coupling and
| high cohesion.
| s5806533 wrote:
| Low coupling and high cohesion were the terms that I was
| thinking of, too.
___________________________________________________________________
(page generated 2022-02-03 23:02 UTC)