惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
P
Privacy & Cybersecurity Law Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
C
Cybersecurity and Infrastructure Security Agency CISA
S
Security Affairs
Latest news
Latest news
Security Latest
Security Latest
N
News and Events Feed by Topic
Spread Privacy
Spread Privacy
P
Proofpoint News Feed
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
The Exploit Database - CXSecurity.com
The Last Watchdog
The Last Watchdog
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Vulnerabilities – Threatpost
Hacker News - Newest:
Hacker News - Newest: "LLM"
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
The Cloudflare Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
GRAHAM CLULEY
博客园_首页
S
Secure Thoughts
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
AWS News Blog
AWS News Blog
腾讯CDC
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
N
News and Events Feed by Topic
A
Arctic Wolf
MongoDB | Blog
MongoDB | Blog
爱范儿
爱范儿
Project Zero
Project Zero
A
About on SuperTechFans
罗磊的独立博客
云风的 BLOG
云风的 BLOG
Know Your Adversary
Know Your Adversary
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
K
Kaspersky official blog
L
LINUX DO - 最新话题
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
F
Fortinet All Blogs

Kent C. Dodds Blog

Implementing Hybrid Semantic + Lexical Search Simplifying Containers with Cloudflare Sandboxes Migrating to Workspaces and Nx Offloading FFmpeg with Cloudflare Building Semantic Search on my Content Helping YOU ask ME questions with AI How I used Cursor to Migrate Frameworks The Dow's Start on the Covenant Path 2025 in Review The next chapter: EpicAI.pro AI is taking your job How I increased my visibility Launching Epic Web 2023 in Review Stop Being a Junior RSC with Dan Abramov and Joe Savona Live Stream Fixing a Memory Leak in a Production Node.js App 2022 in Review My Car Accident I Migrated from a Postgres Cluster to Distributed SQLite with LiteFS I'm building EpicWeb.dev A review of my time at Remix Remix: The Yang to React's Yin How I help you build better websites Why I Love Remix The State Initializer Pattern How to React ⚛️ Get a catch block error message with TypeScript Building an awesome image loading experience How Remix makes CSS clashes predictable Introducing the new kentcdodds.com How I built a modern website in 2021 How to use React Context effectively Static vs Unit vs Integration vs E2E Testing for Frontend Apps The Testing Trophy and Testing Classifications Array reduce vs chaining vs for loop Don't Solve Problems, Eliminate Them Super Simple Start to Remix Super Simple Start to ESModules in Node.js JavaScript Pass By Value Function Parameters How to write a Constrained Identity Function (CIF) in TypeScript How to optimize your context value How to write a React Component in TypeScript TypeScript Function Syntaxes Listify a JavaScript Array Build vs Buy: Component Libraries edition Using fetch with TypeScript Wrapping React.useState with TypeScript Define function overload types with TypeScript 2020 in Review Business and Engineering alignment Hi, thanks for reaching out to me 👋 useEffect vs useLayoutEffect Super simple start to Firebase functions Super simple start to Netlify functions Super Simple Start to css variables Favor Progress Over Pride in Open Source Testing Implementation Details How getting into Open Source has been awesome for me useState lazy initialization and function updates Use ternaries rather than && in JSX Application State Management with React Use react-error-boundary to handle errors in React JavaScript to Know for React How I structure Express apps What open source project should I contribute to? When I follow TDD AHA Programming 💡 How I Record Educational Videos Should I write a test or fix a bug? Stop mocking fetch Intentional Career Building Improve test error messages of your abstractions Tracing user interactions with React Eliminate an entire category of bugs with a few simple tools Common mistakes with React Testing Library Super Simple Start to React Stop using client-side route redirects The State Reducer Pattern with React Hooks Function forms Replace axios with a simple custom fetch wrapper How to test custom React hooks React Production Performance Monitoring Should I useState or useReducer? Stop using isLoading booleans Make Your Test Fail Make your own DevTools An Argument for Automation Fix the "not wrapped in act(...)" warning Super Simple Start to ESModules in the Browser Implementing a simple state machine library in JavaScript 2010s Decade in Review Why users care about how you write code Why I avoid nesting closures Don't call a React function component Why your team needs TestingJavaScript.com Inversion of Control Understanding React's key prop How to Enable React Concurrent Mode Profile a React App for Performance
Colocation
2019-06-17 · via Kent C. Dodds Blog

We all want to have codebases that are easy to maintain, so we start out with the best of intentions to make our codebase (or our corner of the codebase) maintainable and easy to understand. Over time, as a codebase grows, it can become more and more difficult to manage dependencies (JS, CSS, images, etc.). As projects grow, an increasing amount of your codebase becomes "tribal knowledge" (knowledge that only you or a few others are privy to) and this sort of knowledge contributes to "technical debt" (whether that term is accurate or not).

I like to keep my codebases manageable for not only me (the one who wrote it), but also my teammates, future maintainers, and myself in 6 months. I think we can all agree that this is a great ideal that we should strive for in our codebases. There are a lot of different tools and techniques at our disposal to accomplish this.

I don't want to discuss whether to comment your code (you should) and what your comments should be about (You explain why you're doing something unexpected in the comments so people coming after can understand the decisions that were made which resulted in the unexpected or odd code). (Ok, maybe I did want to talk about that a little). Instead I want to focus on where those code comments are placed. We generally "co-locate" these comments with the code they're explaining by putting it as close as possible to the relevant code.

Consider for a minute, if we did this differently. What if we place those comments in a totally separate file. A massive DOCUMENTATION.md file or perhaps even a docs/ directory that maps back to our src/ directory. Sound like fun to you? Yeah, not to me either. There would be some serious problems we'd encounter by not co-locating our comments with the code it's explaining.

  • Maintainability: They'd get out of sync or out of date quicker (than they already do). We'd move or delete a src/ file without updating the corresponding docs/ file.
  • Applicability: People looking at the code in src/ might miss an important comment in docs/ or not comment their own code because they don't realize that there's an existing docs/ file for the src/ file they're editing.
  • Ease of use: Context switching from one location to the next would be a challenge with this kind of a set up as well. Having to deal with multiple locations for files could make it difficult to ensure you have everything you need to maintain a component.

We could definitely come up with a convention for this kind of code commenting style, but why would we want to? Isn't it simpler to keep the comments co-located with the code they're explaining?

So what?

Now, you're probably thinking to yourself: "Yeah, duh, this is why nobody does this docs/ thing and everyone just co-locates their comments with the code. That's obvious. What's your point?" My point is that the benefits of co-location are everywhere.

HTML/View

Take HTML for example. All the benefits of co-locating our comments translate over to our templates as well. Before modern frameworks like React, you'd have your view logic and your view templates in totally separate directories. This falls prey to the same problems described above. These days it's far more common to put these things in the exact same file with React and Vue for example. With Angular if it's not in the same file, the template file is at least right next to the JS file with which it is associated.

CSS

Another concept this applies well to is CSS. I'm not going to argue with you about the merits of CSS-in-JS (it's fantastic), but the benefits are out of this world. Learn more here.

Tests

This concept of file co-location applies great to unit tests as well. How common is it to find a project with a src/ directory and a test/ directory filled with unit tests that attempts to mirror the src/ directory? All the pitfalls described above apply here as well. I probably wouldn't go as far as putting the unit tests in the exact same file, but I don't totally rule that out as an interesting idea either (the implementation is left as an exercise to the reader).

To help enable a more maintainable codebase, we should co-locate our tests files with the file or group of files they are testing. This ensures that when new people (or myself in 6 months) come to the code, they can see immediately that the module is tested and use those tests as a reference to learn about the module. When they make changes, it reminds them to update (add/remove/modify) the tests to account for their changes.

State

Application/Component state experiences the same benefits. The more disconnected/indirect your state is from the UI that's using it, the harder it is to maintain. Localizing state has even more benefits than maintainability, it also improves the performance of your application. A state change in one corner of your application component tree will re-render a lot fewer components than a state change at the top of the tree. Localize your state.

"Reusable" utility files

This applies to "utility" files and functions as well. Imagine you're writing a component and see a nice bit of code that could be extracted into its own function. You extract it and think: "Huh... I'll bet a lot of people could use this." So you pull it out and put it into your app's utils/ directory and move on with your life.

Later, your component is deleted, but the utility you wrote is out of sight, out of mind and it remains (along with its tests) because the person who deleted it assumed it was more widely used. Over the years, engineers work hard to make sure that the function and its tests continue to run and function properly without even realizing that it's no longer needed at all. Wasted effort and cognitive load.

If instead you had just left that function directly in the file that used it, the story would be completely different. I'm not saying don't bother unit testing complex utility functions (please do), but keeping them closer to where they're needed helps you avoid problems.

And for heaven's sake, please DELETE THIS ESLINT RULE and all rules like it.

The principle

The concept of co-location can be boiled down to this fundamental principle:

Place code as close to where it's relevant as possible

You might also say: "Things that change together should be located as close as reasonable." (Dan Abramov said something like this to me once).

Open Source made easy(-er)

Aside from avoiding the problems discussed earlier, there are other benefits to structuring your projects this way. Taking a component and turning it into an open source project is often as simple as copy/pasting the folder to another project and publishing that to npm. Then you just install it in your project and update your require/import statements and you're good to go.

Exceptions

Sure there's a good argument for documentation that spans the whole or part of a system and how things integrate together. And where would you put integration or end-to-end tests that span across components? You might think those are exceptions, but they can actually subscribe nicely to the principle mentioned above

If I have a part of my app associated with user authentication and I want to document that flow, I can put a README.md file in the folder that has all of the modules associated with user authentication. If I need to write integration tests for that flow, I could place the file for those tests in that same folder.

For end-to-end tests, those generally make more sense to go at the root of the project. They span beyond the project itself and into other parts of the system, so it makes sense to me for those to be in a separate directory. They don't really map to the src/ files. In fact, E2E tests don't really care how the src/ is organized at all. Refactoring and moving around files in the src/ directory should not necessitate changing the E2E tests at all.

Conclusion

Our goal here is to build software that is as simple to maintain as possible. The same benefits of maintainability, applicability, and ease of use we get from co-locating our comments we get by co-location of other things as well. If you've never tried it out, I recommend you give it a shot.

P.S. If you're concerned about violating "separation of concerns" I recommend you check out this talk by Pete Hunt and re-evaluate what that means 😀.

P.P.S. I should also note that this applies great to images and really any other resource as well. And when you use a tool like webpack, co-locating those resources is crazy easy too. Honestly, this is one of the core value propositions of webpack IMO.