All Articles

Shared code: Learning lessons from old-school development

Background

Chances are, you’ve found yourself copying and pasting code from one project to another at some point in your career. Most of us have been there, and if you’re like me you probably find yourself wondering if there’s a better way.

In this article I’ll be working with the example of sharing high-level, generic React components between projects. But really, these techniques can be applied to a wide variety of scenarios.

First, let’s look at the obvious options and why they tend to be problematic. (Skip to the solution section if you don’t care).

1. Treating your generic code as a library

Using the example of React components, there is often a temptation to utilize something like NPM to treat your components as an external dependency. This may work for some use cases, but can quickly become a maintenance nightmare.

One of the biggest annoyances is the inability to make changes ‘in situ’. Tools like Lerna JS attempt to solve this problem but only work when all related code can be stored in a single monorepo. When working with multiple, unrelated projects, you end up having to maintain a significant amount of isolated packages which can grow into a large amount of extra work fairly quickly.

2. Using Git Submodules

Anyone who has attempted to use Git submodules in this way will know how problematic it can be. If you were looking for a way to drive developers insane and create endless headaches, look no further.

3. Third Party Solutions

Tools like Bit have emerged in recent years to address this exact problem. Bit has a number of advantages over say, NPM, in that it allows ‘in-situ’ updates from any codebase where components are used without the need for submodules etc.

However, after using Bit for a period of time, there are still a number of drawbacks:

  • It introduces a completely new tool-chain which adds complexity and additional points of failure.
  • It’s a third party solution and could require paying to use or paying to host yourself.
  • If you just want to cherry pick bits it can get complicated vs copy-paste.

4 Copy/Paste

I know a few teams/individuals (myself included) who end up resorting to a good ol’ manual copy and paste in order to share code.

This option presents a few advantages none of the others have:

  • Taking only what you want from another project is really easy, you don’t have to download the whole “version” or anything.
  • Making changes on your end is fine and doesn’t have to affect anything upstream unless you want it to.
  • There’s zero learning curve and no extra tooling

In fact, copy/pasting is almost perfect. But there’s a few issues that prevent it from being a particularly robust solution:

  • It can be time-consuming when the changes are very extensive and affect multiple files.
  • It can also be very time-consuming/manual when you have changes on both sides and need to merge in said code “by hand”.
  • It’s sometimes hard to tell what has even changed without trawling through the commit histories of both projects or manually diffing the entire directories.

The Solution

The world of Frontend Development in particular seems to love shiny new frameworks and toys. This tendency to reinvent the wheel can be pretty exhausting at times. Especially when the obvious solution that’s been sat in front of us the whole time, isn’t something new. It’s something old.

Patches.

For those who aren’t familiar with patches; some of the most venerable projects that make up the ecosystem we play in today still use a patch-based development workflow. From the Linux kernal to Git itself.

Patches should be the obvious solution. The fact that they often aren’t is probably due to a widespread misunderstanding of git itself.

With the proliferation of monorepos and sites like Github, Gitlab etc, it’s easy to feel like if you’re not constantly checking everything into a central repository, you’re somehow doing it wrong. In fact, it’s probably the opposite.

On the Git website one of the randomised taglines that appears in the header is literally “distributed is the new centralized”. Patches are a robust and well-established tool that provide exactly what we need to organically and flexibly share bits of code between projects.

Using Patches to Share Changes to React Components

Let’s say we have a project creatively named Project A.

Project A is a fairly typical React project. It has a bunch of components in src/components. One of these components is a called CatPicture.

// project_a/src/components/CatPicture.js
import React from 'react'

export const CatPicture = () => (
  <div>
    <img src="https://cdn.pixabay.com/photo/2014/04/13/20/49/cat-323262_1280.jpg" />
  </div>
)

We might decide we also want the CatPicture component in Project B. In this case, we can literally copy and paste it into the project.

CatPicture serves as a great starting point for Project B but we decide we need to support different colours

// project_b/src/components/CatPicture.js
import React from 'react'

- export const CatPicture = () => (
-   <div>
+ export const CatPicture = ({ color }) => (
+   <div style={{backgroundColor: color}}>
    <img src="https://cdn.pixabay.com/photo/2014/04/13/20/49/cat-323262_1280.jpg" />
  </div>
)

Meanwhile, back in Project A, a genius, 10x developer finds a way to improve the cat picture.

// project_a/src/components/CatPicture.js
import React from 'react'

export const CatPicture = () => (
  <div>
-     <img src="https://cdn.pixabay.com/photo/2014/04/13/20/49/cat-323262_1280.jpg" />
+     <img src="https://cdn.pixabay.com/photo/2017/11/09/21/41/cat-2934720_1280.jpg" />
  </div>
)

You decide you also want this new and improved cat picture in Project B. Obviously to copy and paste it in this scenario would still be trivial. But if the changes were more numerous and the logic was a lot more complicated you may struggle.

Enter the patch.

Assuming you have both projects cloned to your local computer, creating a patch of the latest commit is super simple. Inside the directory for Project A just run:

git format-patch HEAD^

If you haven’t used format-patch before, you can read the full docs here but all you really need to know is that it creates a patch based on 1 or more commits.

In this case we’re giving it HEAD^ which is short for HEAD^1 (the last 1 commit). If the commit you wanted wasn’t in the last commit you can specify a specific commit with: git format-patch -1 <commit> where -1 is how many commits to include from that point. If you leave out -1 completely and specify a commit SHA it will include all commits from that commit onwards. Pretty handy!

Tip: you can check commit hashes using git log.

Once you’ve run that command you should have a patch file in your current directory. e.g. 0001-Improve-cat-picture.patch. Feel free to cat (no pun intended) the patch file, it’s basically just a plain-text diff like you’d see on Github.

To apply it, it’s as simple as going over to Project B and telling git to apply the patch with git am:

git am ../project_a/001-Improve-cat-picture.patch

This will work exactly the same as when you run git pull. Git will turn the patch into a commit and try apply it automatically. If it detects conflicts it can’t resolve you’ll be asked to intervene.

This technique is great as you can cherry-pick code from wherever you want to wherever you want. There’s no limitations on taking improvements from wherever they originate and propagating them to other codebases. This might sound chaotic, but it’s actually an exceptionally natural way of working. Besides, it works for the 15m+ lines of code in the Linux Kernel without causing anarchy so your React app should be fine 😊

I’d encourage you to play around with git format-patch and git am as they have some really cool options. For example, if your folders are structured differently between your codebases you can tell git where to look with things like prefixes, relative folders etc.

Finishing Up

Hopefully this article was useful. If this got your juices flowing to learn more Git and patch-based workflows, check out Drew DeVault’s fantastic interactive tutorial on using git send-email which lets you automatically send patches to people via email with one command!