<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Johnson</title>
    <description>The latest articles on DEV Community by David Johnson (@davidisnotnull).</description>
    <link>https://dev.to/davidisnotnull</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F87158%2Fc55fee2a-2b7b-43f8-8d10-7b10de2f8335.jpg</url>
      <title>DEV Community: David Johnson</title>
      <link>https://dev.to/davidisnotnull</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davidisnotnull"/>
    <language>en</language>
    <item>
      <title>What Went Well: Nothing. What Could We Improve: This Meeting.</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Wed, 18 Mar 2026 09:31:12 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/what-went-well-nothing-what-could-we-improve-this-meeting-2h87</link>
      <guid>https://dev.to/davidisnotnull/what-went-well-nothing-what-could-we-improve-this-meeting-2h87</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxgbekjuytaqreeynzek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxgbekjuytaqreeynzek.png" alt="What Went Well: Nothing. What Could We Improve: This Meeting." width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Picture the scene. It's Friday afternoon. The sprint is over. Someone has opened a Miro board with three columns: &lt;em&gt;What went well&lt;/em&gt;, &lt;em&gt;What didn't go well&lt;/em&gt;, &lt;em&gt;What could we improve&lt;/em&gt;. People are staring at it like it owes them money. After a long pause, someone types "communication" under the third column. Someone else adds "deployment process." A third person types "communication" again, not having noticed the first one.&lt;/p&gt;

&lt;p&gt;Forty-five minutes later, you close the meeting with a vague commitment to "talk more" and "look at the pipeline." No owner. No date. No follow-up. The action items go into a Confluence page that no one will open until the next time someone asks what the last retro produced, at which point someone will say "I think we looked at the pipeline."&lt;/p&gt;

&lt;p&gt;This is most retrospectives. Not all of them. But most.&lt;/p&gt;

&lt;p&gt;The retrospective is, in theory, one of the most valuable rituals in software delivery. A structured moment of collective honesty. A chance to ask &lt;em&gt;what's actually happening here&lt;/em&gt;, and to improve as a team rather than just continue. In practice, it is often a compliance exercise dressed up as a ceremony. You do it because the process says to. And because you do it badly, it produces nothing. And because it produces nothing, people stop engaging. And because people stop engaging, it produces nothing. Round and round.&lt;/p&gt;

&lt;p&gt;The failure isn't usually dramatic. There's no single villain. The meeting fails slowly, through accumulated habits that look reasonable in isolation.&lt;/p&gt;

&lt;p&gt;The most common is what I'd call &lt;strong&gt;honesty suppression&lt;/strong&gt;. A retro only works if people say true things. But teams are made of humans with memories and working relationships and line managers in the room. Psychological safety isn't a switch you flip, it's an environment you either build over months or don't. Ask a team that doesn't trust each other what went wrong this sprint, and they will tell you what they think is safe to say. Which is usually "communication."&lt;/p&gt;

&lt;p&gt;Close behind that is the retrospective that confuses symptoms for causes. "The deployment took too long" is a symptom. What caused it? What broke? Was it knowledge concentrated in one person? A manual step that's never been automated because no one owns it? Symptoms are easy to identify and almost useless to act on. Causes require interrogation.&lt;/p&gt;

&lt;p&gt;Then there's the retrospective that generates actions and buries them. Twelve items on the board, three owners, no deadlines, never reviewed. The next sprint starts and everyone is heads-down immediately, and the actions exist in a document in a folder in a space that no one has the Confluence breadcrumb for anymore. This is arguably worse than not writing anything down. At least then you have no expectation of change.&lt;/p&gt;

&lt;p&gt;And finally, the retro that happens to the team rather than with it. A facilitator running through a template, people dutifully adding sticky notes, no real conversation, no challenge, no tension. If the meeting could be replaced by a Google Form and nobody would notice, that's a sign.&lt;/p&gt;

&lt;p&gt;Good retrospectives don't feel like good retrospectives. They feel uncomfortable, occasionally.&lt;/p&gt;

&lt;p&gt;Not gratuitously uncomfortable. Nobody needs conflict for its own sake. But the useful retrospective will surface something that needs to be named. A process that is quietly eating everyone's time. A decision that was made three months ago that's become a millstone. A pattern in how the team communicates under pressure. If you come out of a retro feeling exactly as you went in, nothing happened.&lt;/p&gt;

&lt;p&gt;The facilitator matters more than the format. Whether it's Start / Stop / Continue, Four Ls, the Sailboat, or something you made up yourself - the format is scaffolding, not substance. What actually produces insight is a facilitator who asks the second question. The first question, &lt;em&gt;what didn't go well?,&lt;/em&gt; is easy. The second one, &lt;em&gt;why? what's the root of that? where does that actually come from?&lt;/em&gt;, is where the retro earns its keep. This is why a rotating facilitator, pulled from the team, works better than a tired Scrum Master running through a template for the fortieth consecutive sprint. Familiarity breeds shortcuts.&lt;/p&gt;

&lt;p&gt;The other thing good retros do is respect the difference between things the team can change and things they can't. Teams waste enormous energy relitigating decisions made above their authority. That's not a retro, it's group therapy. Acknowledge it, park it, escalate it if you need to, but don't spend your one improvement meeting venting about the roadmap you had no hand in setting. Focus on the things you can actually move.&lt;/p&gt;

&lt;p&gt;Outcomes deserve more attention than format. A retro that surfaces three real insights and produces one actionable change is worth more than a retro that generates twelve items that dissolve by the start of the next week.&lt;/p&gt;

&lt;p&gt;An action from a retrospective should have three things: an owner, a definition of done, and a date. Not "the team will look at the CI pipeline." Someone owns it. Something specific will change. By a named sprint or date. If you can't say those three things about an item, it's not an action, it's a wish. And wishes are washy.&lt;/p&gt;

&lt;p&gt;The actions should be reviewed at the start of the &lt;em&gt;next&lt;/em&gt; retro, before anything else. Not as bureaucratic ritual, but because the quality of your follow-through is the only signal the team has that the retro is real. The moment people believe that actions disappear into the void, engagement drops. And once it drops, getting it back is hard.&lt;/p&gt;

&lt;p&gt;One practice I've seen work well: cap the number of actions. Three. Possibly five if they're trivial. Not twelve. The instinct to write everything down is understandable. It feels productive, but a team can only absorb so much change per sprint. Three things, actually done, compounds beautifully. Twelve things, mostly ignored, erodes trust in the whole process.&lt;/p&gt;

&lt;p&gt;The retrospective is a bet that your team will be better next sprint than it was last sprint. Not because the sprint was magical, but because you stopped, looked honestly at what happened, identified something specific to change, and then changed it.&lt;/p&gt;

&lt;p&gt;That bet only pays out if you take it seriously. Not solemnly. Retros can be human and I do try to make them funny. But they must be taken &lt;em&gt;seriously&lt;/em&gt;, in the sense that the conversation is real, the actions are real, and the follow-through is real.&lt;/p&gt;

&lt;p&gt;Most teams don't do that. They run the meeting, fill the board, close the tab, and wonder why nothing gets better.&lt;/p&gt;

&lt;p&gt;You know what to do instead. The hard part isn't the format. It's actually meaning it.&lt;/p&gt;

</description>
      <category>process</category>
      <category>devex</category>
    </item>
    <item>
      <title>The Firebreak</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Mon, 16 Mar 2026 10:57:00 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/the-firebreak-1oej</link>
      <guid>https://dev.to/davidisnotnull/the-firebreak-1oej</guid>
      <description>&lt;p&gt;Go-live day has a very specific energy. There's the final pre-launch checks. DNS propagating, smoke tests passing, someone refreshing the homepage on their phone like it might disappear if they stop looking. Then there's the moment the client says "it's live" in Slack, followed by the gif with the champagne. Everyone types "🎉" in response. You close seventeen browser tabs.&lt;/p&gt;

&lt;p&gt;And then, almost immediately, someone opens a ticket.&lt;/p&gt;

&lt;p&gt;It starts with the low-grade stuff. A padding issue on mobile that nobody caught in staging because the test devices were wrong. A redirect that was supposed to be a 301 but is doing something weird in Safari. A form confirmation email landing in junk because the SPF record wasn't quite right. Nothing catastrophic. Just the natural static that comes with deploying anything to the actual internet with actual users.&lt;/p&gt;

&lt;p&gt;The instinct, in most teams, is to triage it all, close the sprint, and start the next one. There's a backlog. There are features that were descoped to hit the launch date. The client is energised and has Ideas. Momentum feels like the right thing to maintain.&lt;/p&gt;

&lt;p&gt;This is where a lot of teams quietly make a mistake.&lt;/p&gt;

&lt;p&gt;Foresters have a concept called a firebreak. A strip of land that's been deliberately cleared of vegetation to slow or stop the spread of a wildfire. You don't cut it during the fire. You cut it in advance, during a calm period, because you know the fire is coming and you want to contain the damage when it does.&lt;/p&gt;

&lt;p&gt;In software, a firebreak is borrowed from the same idea. It's a short, deliberately protected period after a major release where the team stops building new things and instead deals with everything that the release exposed, deferred, or broke. Not sprint zero. Not a bug bash. A structured pause with a clear purpose: get the new thing properly stable before you start piling new weight on top of it.&lt;/p&gt;

&lt;p&gt;The typical firebreak runs a week, sometimes two. Occasionally just three or four days is enough. The length is almost less important than the protection. It only works if the stakeholders agree that no new feature work starts during this window. That part requires a conversation before launch, not after. "After we go live, we're going to take a week to stabilise before we start the next phase" is a much easier sell than "we need to pause the roadmap because things aren't quite right." The first is professionalism. The second sounds like an apology.&lt;/p&gt;

&lt;p&gt;What actually happens during a firebreak depends on what the launch revealed, but there are patterns.&lt;/p&gt;

&lt;p&gt;The monitoring always needs attention. You had alerting in place, you think, but now you're watching real traffic and the dashboards look different to how they looked in staging. Error rates that seemed fine at low volume need different thresholds at scale. Slow queries that you knew about but didn't prioritise are now visibly dragging the 95th percentile. You spend time on this because fixing a performance problem you can see is significantly easier than diagnosing one in the early hours of the morning six weeks later.&lt;/p&gt;

&lt;p&gt;The documentation that was going to be written "after launch" gets written. App passports, deployment runbooks, environment variable inventories, the things that live in someone's head because there was never a good time to get them out. There is now a good time. Use it, because that person is going to leave eventually (everyone does), and "I think James set that up" is not an operational strategy.&lt;/p&gt;

&lt;p&gt;Accessibility issues get a proper pass. In the crush to ship, the audit findings from three weeks ago got split into "critical" and "nice to have," and the nice-to-haves are sitting in the backlog. During a firebreak you make actual progress on them rather than letting them drift. This is the right thing to do ethically. It's also the right thing to do because accessibility debt compounds in the same way technical debt does, and nobody enjoys an enforcement notice.&lt;/p&gt;

&lt;p&gt;The team holds a proper retrospective, not the fifteen-minute end-of-sprint version but a genuine post-launch review. What worked. What nearly killed us. What we promised ourselves we'd never do again and then did anyway. These conversations are worth having while the launch is fresh and before everyone has moved on to the next thing.&lt;br&gt;
The lessons that get captured in the week after a launch are significantly more honest than the ones captured a month later, when the rough edges of memory have been sanded smooth by distance.&lt;/p&gt;

&lt;p&gt;Security gets a look. Not a full penetration test necessarily, but someone actually checking whether the production environment has hardened headers, whether debug mode is genuinely off, whether the admin endpoints are properly restricted. Launch day is not the moment for this. The firebreak is.&lt;/p&gt;

&lt;p&gt;It's important to acknowledge that a firebreak isn't rest.  The name makes it sound passive. A pause. A gap. But the work is real and it matters. The difference is that you're not building forward; you're consolidating what you have.&lt;/p&gt;

&lt;p&gt;In forestry, a firebreak doesn't stop fires happening. It controls where they can go. It limits the damage when things go wrong. And things go wrong, in forests and in production environments, with depressing regularity.&lt;/p&gt;

&lt;p&gt;What you're really doing during a firebreak is buying down risk before you start accumulating more of it. Every new feature you add to an unstable foundation makes the foundation harder to fix. Every week that passes without documentation is a week of institutional memory at risk. Every unresolved performance issue is a potential incident waiting for the right traffic spike to mature.&lt;/p&gt;

&lt;p&gt;The confetti has landed. The client is happy. The team deserves a moment to breathe, and then a moment to tidy.&lt;/p&gt;

&lt;p&gt;Do the firebreak. Cut the break before the fire, not during it.&lt;/p&gt;

</description>
      <category>devex</category>
      <category>productmanagement</category>
    </item>
    <item>
      <title>A simple webpack setup with Bootstrap and VueJS</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Fri, 28 Feb 2025 13:44:21 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/a-simple-webpack-setup-with-bootstrap-and-vuejs-59lp</link>
      <guid>https://dev.to/davidisnotnull/a-simple-webpack-setup-with-bootstrap-and-vuejs-59lp</guid>
      <description>&lt;p&gt;I'm sure there a hundreds of variants of these out there in the wild, but I thought I'd throw my aging hat into the ring. Or keys into the hat. What's the saying?&lt;/p&gt;

&lt;p&gt;This is something I've boiled down to be as light as I could possibly make it, and as straightforward to work with as I could get. Although critique is always welcome. Constructive critique. Don't just wade in here, call me a muppet, and then ride off into the sunset.&lt;/p&gt;

&lt;p&gt;I've put a link to the Github repository at the end, so you can skip past everything if you just want the raw goods.&lt;/p&gt;

&lt;h1&gt;
  
  
  Node version management
&lt;/h1&gt;

&lt;p&gt;First thing to talk about - Node.js. I expect many of you out there working on front-end development need to switch node versions between different projects. It's a given. Of course, we need a way to manage that, and I used to use NVM. &lt;/p&gt;

&lt;p&gt;However, the past few months I've been trying out something called FNM, or &lt;em&gt;Fast Node Manager&lt;/em&gt;, and I really like it. It's built in Rust, and if you want to give it a go, there's a very descriptive Github repo here &lt;a href="https://github.com/Schniz/fnm" rel="noopener noreferrer"&gt;https://github.com/Schniz/fnm&lt;/a&gt; that explains how to configure it.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's in the box?
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Firstly, the packaging
&lt;/h2&gt;

&lt;p&gt;This is my &lt;code&gt;package.json&lt;/code&gt;. I'm running it in Node.js 22.14.0, and there is a &lt;code&gt;.node-version&lt;/code&gt; file in the project that should automatically install the correct node variant if you're running FNM with &lt;code&gt;--use-on-cd&lt;/code&gt;. (I won't explain that here, it's in the repo referenced above).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"frontend-build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Webpack build for the Bootstrap and VueJS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"davidisnotnull"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"webpack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"boilerplate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"setup"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@fortawesome/fontawesome-free"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.2.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@fortawesome/fontawesome-svg-core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.2.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@fortawesome/free-solid-svg-icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.2.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@fortawesome/vue-fontawesome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@popperjs/core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.11.6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"axios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.1.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.2.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jquery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.6.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jquery-validation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.19.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"jquery-validation-unobtrusive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mitt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"vue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.2.41"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"vue-axios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.5.2"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@babel/core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.21.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@babel/preset-env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.19.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"assets-webpack-plugin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoprefixer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^10.4.12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"babel-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8.2.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"clean-webpack-plugin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"css-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.7.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mini-css-extract-plugin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.7.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"postcss-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.55.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sass-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^13.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"style-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.3.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"vue-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^17.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"webpack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.78.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"webpack-bundle-analyzer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.6.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"webpack-cli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.10.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"webpack-merge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.8.0"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=22"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webpack --config ./configuration/webpack.dev.config.js --mode=development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"watch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webpack --config ./configuration/webpack.dev.config.js --mode=development --watch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bundle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm install &amp;amp;&amp;amp; npm run watch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webpack --config ./configuration/webpack.production.config.js --mode=production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"stats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webpack --config ./configuration/webpack.production.config.js --mode=production --json &amp;gt; dist/stats.json &amp;amp;&amp;amp; webpack-bundle-analyzer dist/stats.json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt; version of the build. That's kind of self-explanatory, but use &lt;code&gt;dev&lt;/code&gt; when you're dev'ing so you can debug things easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter webpack
&lt;/h2&gt;

&lt;p&gt;I've used &lt;code&gt;webpack-merge&lt;/code&gt; so that I can create config variants for development and production. You'll see these in the &lt;code&gt;configuration&lt;/code&gt; folder in the repo.&lt;/p&gt;

&lt;p&gt;The main bare-bones webpack config looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./configuration/environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AssetsPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CleanWebpackPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clean-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VueLoaderPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MiniCssExtractPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mini-css-extract-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js/app.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scss/app.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;    
    &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;js|jsx&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/node_modules/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;babel-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;css$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;},&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;css-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;scss&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="nx"&gt;MiniCssExtractPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;css-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postcss-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;postcssOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                                        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autoprefixer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;];&lt;/span&gt;
                                &lt;span class="p"&gt;}&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sass-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;vue$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;woff&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;2&lt;/span&gt;&lt;span class="se"&gt;)?&lt;/span&gt;&lt;span class="sr"&gt;|ttf|eot|svg&lt;/span&gt;&lt;span class="se"&gt;)(\?&lt;/span&gt;&lt;span class="sr"&gt;v=&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)?&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name].[ext]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="na"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fonts/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js/[name].[contenthash].js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dist/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ProvidePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jquery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jquery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CleanWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;cleanOnceBeforeBuildPatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!stats.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MiniCssExtractPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;css/[name].[contenthash].css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VueLoaderPlugin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AssetsPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack.assets.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;prettyPrint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;optimization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;minimize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.jsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This includes a compiler for Vuejs as well, as I've been using that for some simple interactive functionality. I like React, but I find it a bit too heavy for a lot of the simpler stuff that clients tend to ask for. Unless someone wants an SPA, I find myself leaning into Vue a lot more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The great babel fish
&lt;/h2&gt;

&lt;p&gt;I've got babel configured to only target modern browsers. No IE11 fixes required these days. I am so glad that old Trident engine is dead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/preset-env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Target modern browsers (&amp;gt;0.25% market share and not obsolete)&lt;/span&gt;
          &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;0.25%, not dead&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// Enables polyfill injection based on your code usage (and your browser targets)&lt;/span&gt;
          &lt;span class="na"&gt;useBuiltIns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;corejs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3.28&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// Apply bug fixes from Babel's plugin layer&lt;/span&gt;
          &lt;span class="na"&gt;bugfixes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scripting and styling
&lt;/h2&gt;

&lt;p&gt;There's an &lt;code&gt;app.js&lt;/code&gt; and an &lt;code&gt;app.scss&lt;/code&gt; file in their respective folders in the repo. These, again, are barebones and just import the components that you need to get going.&lt;/p&gt;

&lt;p&gt;Extend these as you need to.&lt;/p&gt;

&lt;p&gt;That's pretty much it. PostCSS and Sass Linting are both light-touch configured, so you've got scope to modify those as you like as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Now go grab the repo
&lt;/h1&gt;

&lt;p&gt;I've structured all of this into a public repo on Github. Please use it to your heart's content. Fork it. Recommend changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidisnotnull/webpack-build-framework" rel="noopener noreferrer"&gt;https://github.com/davidisnotnull/webpack-build-framework&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;David&lt;/p&gt;

</description>
      <category>webpack</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How To Add Swap Space on Ubuntu 22.04</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Thu, 23 Jan 2025 21:35:17 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/how-to-add-swap-space-on-ubuntu-2204-238f</link>
      <guid>https://dev.to/davidisnotnull/how-to-add-swap-space-on-ubuntu-2204-238f</guid>
      <description>&lt;p&gt;One way to guard against out-of-memory errors in applications is to add some swap space to your server. In this guide, we will cover how to add a swap file to an Ubuntu 22.04 server.&lt;/p&gt;

&lt;p&gt;Swap is a portion of hard drive storage that has been set aside for the operating system to temporarily store data that it can no longer hold in RAM. This lets you increase the amount of information that your server can keep in its working memory, with some caveats. The swap space on the hard drive will be used mainly when there is no longer sufficient space in RAM to hold in-use application data.&lt;/p&gt;

&lt;p&gt;The information written to disk will be significantly slower than information kept in RAM, but the operating system will prefer to keep running application data in memory and use swap for the older data. Overall, having swap space as a fallback for when your system’s RAM is depleted can be a good safety net against out-of-memory exceptions on systems with non-SSD storage available.&lt;/p&gt;

&lt;p&gt;We can see if the system has any configured swap by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo swapon --show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it has one, we're going to want to get rid of it and create a new one. You can do that with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo swapoff -v /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then remove the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo rm /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we want to create a new swap file. We'll set the size parameter to 8G as we want to use some VMware in Ubuntu&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo fallocate -l 8G /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the file only accessible to root by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chmod 600 /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now mark the file as swap space by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkswap /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After marking the file, we can enable the swap file, allowing our system to start using it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo swapon /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our recent changes have enabled the swap file for the current session. However, if we reboot, the server will not retain the swap settings automatically. We can change this by adding the swap file to our /etc/fstab file.&lt;/p&gt;

&lt;p&gt;Back up the /etc/fstab file in case anything goes wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cp /etc/fstab /etc/fstab.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the swap file information to the end of your /etc/fstab file by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a few options that you can configure that will have an impact on your system’s performance when dealing with swap.&lt;/p&gt;

&lt;p&gt;The swappiness parameter configures how often your system swaps data out of RAM to the swap space. This is a value between 0 and 100 that represents a percentage.&lt;/p&gt;

&lt;p&gt;With values close to zero, the kernel will not swap data to the disk unless absolutely necessary. Remember, interactions with the swap file are “expensive” in that they take a lot longer than interactions with RAM and they can cause a significant reduction in performance. Telling the system not to rely on the swap much will generally make your system faster.&lt;/p&gt;

&lt;p&gt;Values that are closer to 100 will try to put more data into swap in an effort to keep more RAM space free. Depending on your applications’ memory profile or what you are using your server for, this might be better in some cases.&lt;/p&gt;

&lt;p&gt;We can see the current swappiness value by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat /proc/sys/vm/swappiness
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a Desktop, a swappiness setting of 60 is not a bad value. For a server, you might want to move it closer to 0.&lt;/p&gt;

&lt;p&gt;We can set the swappiness to a different value by using the sysctl command.&lt;/p&gt;

&lt;p&gt;For instance, to set the swappiness to 10, we could type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo sysctl vm.swappiness=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setting will persist until the next reboot. We can set this value automatically at restart by adding the line to our /etc/sysctl.conf file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/sysctl.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the bottom, you can add &lt;code&gt;vm.swappiness=10&lt;/code&gt;. Save and close the file when you are finished (in nano, press &lt;code&gt;Ctrl+x&lt;/code&gt; and it will prompt you to save before exiting).&lt;/p&gt;

&lt;p&gt;Another related value that you might want to modify is the &lt;code&gt;vfs_cache_pressure&lt;/code&gt;. This setting configures how much the system will choose to cache inode and dentry information over other data.&lt;/p&gt;

&lt;p&gt;Basically, this is access data about the filesystem. This is generally very costly to look up and very frequently requested, so it’s an excellent thing for your system to cache. You can see the current value by querying the proc filesystem again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat /proc/sys/vm/vfs_cache_pressure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As it is currently configured (100), our system removes inode information from the cache too quickly. We can set this to a more conservative setting like 50 by typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo sysctl vm.vfs_cache_pressure=50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, this is only valid for our current session. We can change that by adding it to our configuration file like we did with our swappiness setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/sysctl.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the bottom, add the line that specifies your new value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vm.vfs_cache_pressure=50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>linux</category>
    </item>
    <item>
      <title>Is Threads still a thing?</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Tue, 06 Feb 2024 21:03:35 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/is-threads-still-a-thing-434</link>
      <guid>https://dev.to/davidisnotnull/is-threads-still-a-thing-434</guid>
      <description>&lt;h4&gt;
  
  
  And how many people are actually using it?
&lt;/h4&gt;

&lt;p&gt;I have never considered using Threads for any kind of social marketing. Out of Meta’s products, I’m pushing content to Facebook, Instagram, and WhatsApp, but something about Threads has always put me off.&lt;/p&gt;

&lt;p&gt;Curious, I decided to do some research to see if my gut instinct about Threads was correct.&lt;/p&gt;

&lt;h3&gt;
  
  
  The rapid decline of Threads
&lt;/h3&gt;

&lt;p&gt;At it’s peak at launch in July 2023, Threads saw 100 million sign-ups in 5 days, the fastest app to ever achieve this milestone. On July 7th 2023, Meta reported 49.3 million daily active users.&lt;/p&gt;

&lt;p&gt;As of January 2024, a mere 6 months later, the estimated DAU (daily active users) was around 10.3 million (that’s a 79% decrease). User engagement also decreased significantly, with the average daily time spent on the app dropping from 21 minutes to just 3 minutes.&lt;/p&gt;

&lt;p&gt;So what happened here? Why did it see the biggest sign up rate in the history of apps, and within 6 months such a significant decline in interest? It’s backed by Meta, and Meta may be a lot of things, but they’re more than experienced in the social media arena.&lt;/p&gt;

&lt;p&gt;The first question that springs to my mind is “do users want yet another ephemeral messaging app?”. I certainly don’t, but I likely don’t fall into the list of personas that Meta had in mind (being a 40-something year old software engineer).&lt;/p&gt;

&lt;p&gt;However, I think it is important to note that Threads doesn’t really offer anything that you won’t find on other, more established platforms. So what’s the unique value proposition?&lt;/p&gt;

&lt;p&gt;My research highlights that there may have been potential privacy concerns. Having to sign up with an Instagram account may have left users wary of data sharing across platforms. Additionally, the ephemeral nature of messages left users unsure about data retention and control.&lt;/p&gt;

&lt;p&gt;Threads also felt distinctly lacking in features. There’s an absence of diverse content categories, focusing mainly on close friends and personal updates. Something that can be fulfilled by pretty much any other flavour of social app on the market right now.&lt;/p&gt;

&lt;p&gt;There’s also a complete vacuum of news, political discussions, or broader community engagement, limiting its appeal to many users. For me, it makes the ecosystem feel quiet. Posting on there felt more like shouting into a cave than interacting with a social app. It’s got the distinct feel of an echo chamber, not helped by the substantially low number of users compared to it’s competitors.&lt;/p&gt;

&lt;p&gt;Timing wise, I’m not convinced that Meta really hit the nail on the head with their design for Threads either, and this is surprising considering their wealth of understanding of social media.&lt;/p&gt;

&lt;p&gt;The growing popularity of short-form video platforms like TikTok and, to some extent, Instagram, offered a more engaging and dynamic experience compared to Threads’ photo-sharing focus. It’s as though they’ve taken a step backwards and created a lightweight version of Instagram with the look and feel of X (formerly Twitter).&lt;/p&gt;

&lt;p&gt;Many celebrities and influencers initially joined Threads, but quickly migrated back to established platforms with larger user bases and wider reach once they realised that they just weren’t getting the same level of engagement. Why waste energy on a platform that isn’t offering you any noticeable returns?&lt;/p&gt;

&lt;p&gt;Without influencers on the platform, that’s one less thing to capture user attention. If you combine that with a total absence of news and community content, it’s no surprise that users abandoned it in their droves.&lt;/p&gt;

&lt;p&gt;I also get the impression that Meta really aren’t giving it any love. Their primary attention seemed to be on developing Instagram and integrating features similar to Threads within it. Raising, again, the question — what was the point of releasing Threads in the first place?&lt;/p&gt;

&lt;p&gt;Maybe they were anticipating that X would fail after Elon Musk bought it in April 2022, and that it’s user base would migrate onto Threads in the aftermath? This is the only purpose for it that I can think of. It would certainly explain the similarities, in both functionality and interface.&lt;/p&gt;

&lt;p&gt;Enough of my esoteric musings on the matter, let’s look at some actual data and compare how Threads looks against it’s competitors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison of Threads’ user base with other social media platforms
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Monthly Active Users (MAU)&lt;/th&gt;
&lt;th&gt;Daily Active Users (DAU)&lt;/th&gt;
&lt;th&gt;Average Age&lt;/th&gt;
&lt;th&gt;Dominant User Group&lt;/th&gt;
&lt;th&gt;Key Features&lt;/th&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;th&gt;Weaknesses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;100M (peak, July 2023)&lt;/td&gt;
&lt;td&gt;10.3M (Jan 2024)&lt;/td&gt;
&lt;td&gt;18-25&lt;/td&gt;
&lt;td&gt;Gen Z&lt;/td&gt;
&lt;td&gt;Ephemeral messaging, close friends focus&lt;/td&gt;
&lt;td&gt;Fast initial growth, Meta integration&lt;/td&gt;
&lt;td&gt;High user decline, limited features, privacy concerns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X (Formerly Twitter)&lt;/td&gt;
&lt;td&gt;253M&lt;/td&gt;
&lt;td&gt;166M&lt;/td&gt;
&lt;td&gt;34-49&lt;/td&gt;
&lt;td&gt;Diverse&lt;/td&gt;
&lt;td&gt;Microblogging, news, real-time updates&lt;/td&gt;
&lt;td&gt;Large user base, established platform&lt;/td&gt;
&lt;td&gt;Competition, toxicity concerns, recent user loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instagram&lt;/td&gt;
&lt;td&gt;2 billion&lt;/td&gt;
&lt;td&gt;1.4 billion&lt;/td&gt;
&lt;td&gt;25-34&lt;/td&gt;
&lt;td&gt;Diverse&lt;/td&gt;
&lt;td&gt;Photo &amp;amp; video sharing, Stories, Reels&lt;/td&gt;
&lt;td&gt;Large user base, diverse content, influencer marketing&lt;/td&gt;
&lt;td&gt;Algorithm-driven feed, privacy concerns, pressure to curate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facebook&lt;/td&gt;
&lt;td&gt;2.91 billion&lt;/td&gt;
&lt;td&gt;1.9 billion&lt;/td&gt;
&lt;td&gt;35-44&lt;/td&gt;
&lt;td&gt;Diverse&lt;/td&gt;
&lt;td&gt;News, connecting with friends &amp;amp; family, groups&lt;/td&gt;
&lt;td&gt;Largest user base, mature platform&lt;/td&gt;
&lt;td&gt;Declining younger users, privacy concerns, information overload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TikTok&lt;/td&gt;
&lt;td&gt;1.5 billion&lt;/td&gt;
&lt;td&gt;1 billion&lt;/td&gt;
&lt;td&gt;16-24&lt;/td&gt;
&lt;td&gt;Gen Z&lt;/td&gt;
&lt;td&gt;Short-form videos, diverse content, viral trends&lt;/td&gt;
&lt;td&gt;Engaging format, large Gen Z audience&lt;/td&gt;
&lt;td&gt;Data privacy concerns, potential addiction issues&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Notes on the above data
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;User base numbers are estimates and can vary depending on source.&lt;/li&gt;
&lt;li&gt;Threads’ user base is significantly smaller than all other listed platforms.&lt;/li&gt;
&lt;li&gt;Whilst similar in platform formats, Threads focuses on close friends and ephemeral messaging, while X offers a wider range of content and real-time updates.&lt;/li&gt;
&lt;li&gt;Instagram offers more diverse content and influencer marketing opportunities, but with potential downsides like curated feeds and privacy concerns.&lt;/li&gt;
&lt;li&gt;Facebook has the largest user base but struggles with declining younger users and privacy concerns.&lt;/li&gt;
&lt;li&gt;TikTok caters to Gen Z with its engaging short-form video format, but raises data privacy concerns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What’s in store for the future of Threads?
&lt;/h3&gt;

&lt;p&gt;It’s a real challenge to try and discern the fate of Threads in the long run. On the one hand, it holds the record for the fastest user sign up in history. It’s also seen a mass exodus of those users with the fraction that remain spending a mere 3 minutes a day on the platform.&lt;/p&gt;

&lt;p&gt;Personally, I’d give it a “do-not-resuscitate” order and call it a day. However, in the spirit of balance, and because I can’t foresee the outcome, let’s take a look at both the optimistic and pessimistic result.&lt;/p&gt;

&lt;h4&gt;
  
  
  Optimistically…
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Meta invests in significant feature additions, introducing unique functionalities beyond ephemeral messaging, like group chats, live streaming, or integration with other platforms.&lt;/li&gt;
&lt;li&gt;Meta incentivizes and retains high-profile users who actively engage with the platform, driving discoverability and user growth.&lt;/li&gt;
&lt;li&gt;Threads carves out a niche as a platform for specific user groups or interests, creating a dedicated and engaged community.&lt;/li&gt;
&lt;li&gt;It expands beyond close friends, allowing broader content categories like news, entertainment, or creator tools, attracting a wider user base.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Pessimistically…
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Meta continues focusing resources on developing Instagram and other core platforms, leaving Threads neglected and under-maintained, resulting in:&lt;/li&gt;
&lt;li&gt;A lack of significant updates and feature additions lead to user boredom and migration to other platforms.&lt;/li&gt;
&lt;li&gt;New or existing platforms offer more engaging features and experiences, attracting users away from Threads.&lt;/li&gt;
&lt;li&gt;Meta decides Threads isn’t strategically important and either sells it or shuts it down altogether.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  In conclusion
&lt;/h3&gt;

&lt;p&gt;Threads’ future depends on Meta’s commitment to development, addressing user concerns, and differentiating itself from competitors. It needs to adapt, innovate, and capture the attention of users in a dynamic and competitive environment.&lt;/p&gt;

&lt;p&gt;If Meta doesn’t take action on this, I can’t help but see the fate of Threads being they quietly switch it off without anyone really noticing.&lt;/p&gt;

&lt;p&gt;I am leaning on the pessimistic side of the outcomes, and certainly won’t be recommending it as a subject of consideration for my clients. Maybe Meta will pull it out of the bag and turn it into something worthwhile. Or maybe it’ll fade away without fanfare and be forgotten as quickly as it arrived.&lt;/p&gt;

</description>
      <category>socialmediamarketing</category>
      <category>threads</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>A decade of using git has taught me to keep a tidy house</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Tue, 09 May 2023 22:48:14 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/a-decade-of-using-git-has-taught-me-to-keep-a-tidy-house-34k9</link>
      <guid>https://dev.to/davidisnotnull/a-decade-of-using-git-has-taught-me-to-keep-a-tidy-house-34k9</guid>
      <description>&lt;h4&gt;
  
  
  My strategy to help look after your git
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxieaa1d8u21skukq8cwe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxieaa1d8u21skukq8cwe.png" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re in the world of software development, then you’ll likely have come across this thing called “source control”.&lt;/p&gt;

&lt;p&gt;I’m not going to delve into too much detail on what source control is, that’s not why you’re here. This is about my experience using one type of source control called git (as you can probably guess from the title).&lt;/p&gt;

&lt;p&gt;I wrote this after one of my tenures as a Tech Lead in a digital agency. I was weary of seeing the word “fix” as the commit message, of having almost a hundred branches in some repositories, and being completely unable of working out what features or fixes had been included as part of a commit.&lt;/p&gt;

&lt;p&gt;I’m putting the strategy below in the hope that it may help others who find themselves in a similar boat.&lt;/p&gt;

&lt;p&gt;So &lt;em&gt;Ctrl+C, Ctrl+V&lt;/em&gt; to your heart’s content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Strategy
&lt;/h3&gt;

&lt;p&gt;Wherever possible, these guidelines will include a specific justification for each strategy choice. Unjustified guidelines must be either justified or removed.&lt;/p&gt;

&lt;p&gt;Whereas these styles draw mostly from the experience of the author, it also includes ideas from Vincent Dresden.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fixing problems in this branching strategy
&lt;/h4&gt;

&lt;p&gt;Unless otherwise stated, this branching strategy is not optional, nor is it up for interpretation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a guideline is not sufficiently clear, recommend a clearer formulation&lt;/li&gt;
&lt;li&gt;If you don’t like a guideline, try to get it changed or removed; don’t just ignore it. Others might not be aware that you are special and not subject to the same rules as everyone else&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Branching
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Main Branches
&lt;/h4&gt;

&lt;p&gt;At the base of our branching strategy are two evergreen branches —main, and _deve_lop.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Main&lt;/em&gt;, as the name suggests, is our stable branch, and reflects the latest delivered development changes that currently exist in the wild on our production environment. It should not be interacted with in our day-to-day development.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Develop&lt;/em&gt; should always represent the code that is being worked on, ready for the next release. It’s from this branch that we should be creating our feature and release candidate branches.&lt;/p&gt;

&lt;p&gt;Both of these branches must be locked so that force pushes cannot be made to them (cue star wars pun), and standard pushes can only be made via approved pull requests.&lt;/p&gt;

&lt;h4&gt;
  
  
  Release Candidates
&lt;/h4&gt;

&lt;p&gt;These branches contain code that has passed testing and is ready to be rolled out in the next release.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Release branches should be named &lt;strong&gt;release/&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Where the &lt;em&gt;release-identifier&lt;/em&gt; will be the name that we’ve given to this release in whatever task management tool we’re using.&lt;/p&gt;

&lt;p&gt;As soon as the release candidate is ready to deploy to production, we raise a pull request to merge the release candidate into the main branch.&lt;/p&gt;

&lt;h4&gt;
  
  
  Feature Branches
&lt;/h4&gt;

&lt;p&gt;A feature branch comes under the guise of Supporting Branches. These are used to allow for parallel development and to make tracking features easier.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Feature branches should be named &lt;strong&gt;feature/&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Often, we will find ourselves using some sort of task management software, such as Jira. In this case, we need to be naming our feature branches with the identifier of the ticket, followed by a brief description of the what the feature contains. An example could be &lt;strong&gt;feature/DSB-545-marketing-preferences-service&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Highlander’s Law
&lt;/h4&gt;

&lt;p&gt;If you’re old enough to remember that film with Christopher Lambert and Sean Connery, then you’ll probably guess what this is going to be…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There can be only one&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Clearly, we aren’t lopping people’s heads off to cure ourselves of immortality. In this context, Highlander’s Law means that there should only ever be one feature branch for a particular story.&lt;/p&gt;

&lt;p&gt;Never should we see two feature branches created by developers for the same feature. Not only is it deeply confusing for the poor bastard who has to put together a release candidate, but it’s really shoddy practise. One feature branch per feature please.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hotfix Branches
&lt;/h4&gt;

&lt;p&gt;A hotfix branch is branched from main, and should only be used to fix issues that are found in production. Due to the priority nature of hotfixes, they fall outside of the usual release cycle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hotfix branches should be named &lt;strong&gt;hotfix/&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the code in a hotfix branch has been tested and verified, then we raise a pull request to merge the code into the main branch and release to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing code push/pulls
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Fetch before you push
&lt;/h4&gt;

&lt;p&gt;If someone else has been working on the same feature or hotfix branch as the one you’re about to push, make sure you do a fetch to ensure that you’re local copy is in line with what’s out there on the remote.&lt;/p&gt;

&lt;p&gt;You can also turbo charge this process by doing some pruning at the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git fetch origin --prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Rebase, don’t merge
&lt;/h4&gt;

&lt;p&gt;If you’ve checked out a branch and, after doing a fetch, noticed that your local branch is a few commits behind what’s in the remote, don’t merge the remote into your local - &lt;strong&gt;rebase&lt;/strong&gt; it. That way, you’re guaranteed to get a clean copy of what’s on the remote, including the commit history.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pushing code
&lt;/h4&gt;

&lt;p&gt;This one is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NEVER&lt;/strong&gt; push broken code to the remote repo&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No one wants to fix your borked attempt at software development, so don’t do it. If it’s broken, it stays in local until you’ve fixed it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branch Retention
&lt;/h3&gt;

&lt;p&gt;In order to keep the number of branches down to a minimum, it is important to do a little bit of housekeeping after each release to get rid of anything that’s no longer needed. This we call  &lt;strong&gt;pruning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Trust me, there’s nothing worse than opening a repository to see it has a hundred branches. Most of which are stale or no longer relevant.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pruning
&lt;/h4&gt;

&lt;p&gt;After each release, we must perform a branch pruning exercise. The rules of branch pruning are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the release candidate branch is greater than 2 releases ago, then we need to delete it&lt;/li&gt;
&lt;li&gt;If a feature branch has been merged into the current release branch, and it is 0 commits in front of develop, then we need to delete it&lt;/li&gt;
&lt;li&gt;If we have merged a hotfix branch into master and it has been deployed to live after testing, then we need to delete the hotfix branch&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Boglin Branches
&lt;/h4&gt;

&lt;p&gt;If you grew up in the eighties or early nineties, you might remember something called a Boglin. It was like a gremlin, but lived in a swamp.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Boglin branches&lt;/strong&gt; are those that are greater than 60 days old and are any number of commits in front of develop.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They also live in a swamp, of stale code. We have to ask ourselves, if a code change is greater than 60 days old, do we still need it? If we do need it, can we bring it into the upcoming release candidate?&lt;/p&gt;

&lt;p&gt;If we don’t need it, &lt;strong&gt;prune it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If we do need it, then there are two questions that need to be asked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why was it left idle for so long?&lt;/li&gt;
&lt;li&gt;Can we get it tested and potentially bug fixed in time within the current sprint or release cycle?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Commit Messages
&lt;/h3&gt;

&lt;p&gt;A well crafted git commit message is the best way to convey the context of a change to other developers, and your future self. Be kind to your future self, and adhere to the following rules for commit messages.&lt;/p&gt;

&lt;p&gt;As an aside, I’m sick of seeing the word &lt;strong&gt;fix&lt;/strong&gt; as a commit message. Stop it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The rules of commit messages
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Separate the subject from the body
&lt;/h4&gt;

&lt;p&gt;Referring to the git commit manual:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Though not required, it’s a good idea to begin the commit message with a single short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough description. The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git. For example, Git-format-patch(1) turns a commit into email, and it uses the title on the Subject line and the rest of the commit in the body.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Proper casing on a commit message
&lt;/h4&gt;

&lt;p&gt;I don’t think I need to explain any more than the heading for this section — make sure you capitalise the first word on the subject of the git commit, e.g.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Resolve merge conflicts from branch hotfix/BUG-1448&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Do not end the subject line with a period
&lt;/h4&gt;

&lt;p&gt;Trailing punctuation is not necessary on commit messages. Remember, space is precious when we are trying to keep within a 50 character limit.&lt;/p&gt;

&lt;h4&gt;
  
  
  Use the imperative mood in the subject line
&lt;/h4&gt;

&lt;p&gt;Imperative mood simply means “spoken or written as if giving a command or instruction”. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tidy your room&lt;/li&gt;
&lt;li&gt;Take out the bins&lt;/li&gt;
&lt;li&gt;Do your laundry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Being British, we don’t like being outwardly direct or impolite. It’s perfect for commit subject lines, however. One reason for this is that git uses the imperative whenever it creates a git commit on your behalf, and computers aren’t renowned for their politeness (in some cases, neither are developers).&lt;/p&gt;

&lt;p&gt;For example, whenever we do a merge using the git command line, we get:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Merging branch ‘feature/FEA-1234-new-navigation’&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you write in the imperative, all you’re doing is following git’s inbuilt conventions. The following are poor examples of subject messages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fixed bug with &lt;em&gt;X&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;More fixes for broken stuff&lt;/li&gt;
&lt;li&gt;Sexy new API methods&lt;/li&gt;
&lt;li&gt;Changing structure of &lt;em&gt;Y&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt; This is what we call writing in the indicative mood. Being indicative is all about reporting facts, which is not what a commit subject should be — as counterintuitive as that sounds.&lt;/p&gt;

&lt;p&gt;A properly formed git commit subject line should always be able to complete the following sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If applied, this commit will [your git subject line here]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following are good examples of commit subject lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If applied, this commit will &lt;strong&gt;remove deprecated methods&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If applied, this commit will &lt;strong&gt;resolve merge conflicts&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If applied, this commit will &lt;strong&gt;merge pull request #101 from feature/branch&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see how our first few commit messages won’t work in the above format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If applied, this commit will &lt;strong&gt;fixed bug with X&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If applied, this commit will &lt;strong&gt;more fixes for broken stuff&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If applied, this commit will &lt;strong&gt;sexy new API methods&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Use the body to explain “what and why” versus “how”
&lt;/h4&gt;

&lt;p&gt;When we’re looking at commit messages, we aren’t interested so much in how the change has been made, but the reasons for making the change.#&lt;/p&gt;

&lt;p&gt;Here’s an example of a what I would deem a good commit body message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add CPU arch filter scheduler support&lt;/p&gt;

&lt;p&gt;In a mixed environment of running different CPU architectures,&lt;br&gt;&lt;br&gt;
one would not want to run an ARM instance on a X86_64 host and&lt;br&gt;&lt;br&gt;
vice versa.&lt;/p&gt;

&lt;p&gt;This scheduler filter option will prevent instances running&lt;br&gt;&lt;br&gt;
on a host that it is not intended for.&lt;/p&gt;

&lt;p&gt;The libvirt driver queries the guest capabilities of the&lt;br&gt;&lt;br&gt;
host and stores the guest arches in the permitted_instances_types&lt;br&gt;&lt;br&gt;
list in the cpu_info dict of the host.&lt;/p&gt;

&lt;p&gt;The Xen equivalent will be done later in another commit.&lt;br&gt;&lt;br&gt;
The arch filter will compare the instance arch against&lt;br&gt;&lt;br&gt;
the permitted_instances_types of a host&lt;br&gt;&lt;br&gt;
and filter out invalid hosts.&lt;/p&gt;

&lt;p&gt;Also adds ARM as a valid arch to the filter.&lt;/p&gt;

&lt;p&gt;The ArchFilter is not turned on by default.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Tagging Commits
&lt;/h3&gt;

&lt;p&gt;When the code in a particular commit is released to either a test or production environment, we need to tag it. Otherwise, it’s easy to lose track of what code is sitting on what server.&lt;/p&gt;

&lt;p&gt;My recommendation for this is to use the environment name, followed by a hyphen, followed by the date of the release in &lt;em&gt;YYYYmmdd&lt;/em&gt; format. For example, this tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;production-20230421
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indicates that the code for this commit is on production, and was deployed on 21st April 2023.&lt;/p&gt;

</description>
      <category>sourcecontrol</category>
      <category>development</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>My journey in Midjourney</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Wed, 03 May 2023 09:37:33 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/my-journey-in-midjourney-237c</link>
      <guid>https://dev.to/davidisnotnull/my-journey-in-midjourney-237c</guid>
      <description>&lt;p&gt;I've been messing around with Midjourney for a couple of months now. I use the phrase "messing around" because that's exactly what the process has felt like.&lt;/p&gt;

&lt;p&gt;If you've not come across it yet, Midjourney is an AI-powered image generation tool that allows users to create images by providing text and/or image prompts. You use it via installing the Midjourney bot to your Discord, and adding the bot to your text channel.&lt;/p&gt;

&lt;p&gt;My initial testing came out of a curiosity to determine whether I could use Midjourney as a replacement for traditional stock photography in website content or social media posts.&lt;/p&gt;

&lt;p&gt;Just for fun, and the purposes of this article, I went back to one of the first upscaled images I created. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F196f9riexcxxtz83k517.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F196f9riexcxxtz83k517.png" alt="Cats wearing bow ties with a starry night sky" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;/imagine Cats wearing bow ties with a starry night sky&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one I kept for nostalgia. And because cats.&lt;/p&gt;

&lt;p&gt;Getting to grips with the descriptive prompts for Midjourney has been an adventure in itself. I've had some rather horrific failures:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qa351kroad2jd6ucxn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qa351kroad2jd6ucxn2.png" alt="Women alone laughing at salad" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do we remember the "woman laughing at salad" thing from a while back?&lt;/p&gt;

&lt;p&gt;The general structure of a prompt is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{image prompt} {description} {parameters}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;image prompt would be the URL of an image that you want to use as a base, although this isn't mandatory.&lt;/li&gt;
&lt;li&gt;description being your description of the image that you'd like it to generate.&lt;/li&gt;
&lt;li&gt;parameters being the settings that you can add to affect how Midjourney creates the image. I won't dive into these in much detail here, but you can see what's available in the &lt;a href="https://docs.midjourney.com/docs/parameter-list" rel="noopener noreferrer"&gt;Midjourney documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My one piece of advice when it comes to creating the prompts is to experiment. A lot. Try all sorts of different combinations, and don't be shy in getting Midjourney to regenerate the image set that it gives you if it's coming up a bit twisted. Of course, the more random you get with your descriptions, the crazier the results are going to end up.&lt;/p&gt;

&lt;p&gt;Here are my thoughts on what you can consider when writing your own prompts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Be clear in your language&lt;/strong&gt;. The more straightforward and direct you are with your description, the better that the software is going to understand you're requirements. Don't talk to it in the style of Jane Austen or Russell Brand, essentially.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be specific&lt;/strong&gt;. The more detail you add to your prompt, the more it's going to match your expectations. Mention colours, objects, the environment. You've got to give it as much guidance as you can.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talk about the mood&lt;/strong&gt;. If you want your image to be of a certain style, then add that into your prompt. For example (and this is one of my favourites) &lt;em&gt;a futuristic cityscape with neon lights creating a cyberpunk atmosphere&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition&lt;/strong&gt;. Like any good photographer, you're going to have to think about the composition of your image. You can add things like &lt;em&gt;using the rule of thirds&lt;/em&gt; or &lt;em&gt;wide angle shot&lt;/em&gt; to your prompt to help guide the software in creating your perfect image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set your focal point&lt;/strong&gt;. Again, think like a photographer. What's the focus of your image? Try to define your primary subject in your prompt.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've actually found, after some considerable messing about, that Midjourney is a great tool for creating images for social posts (particularly Twitter and Instagram). You can adjust the aspect ratio of the images as well, which is a definite plus when posting to different platforms.&lt;/p&gt;

&lt;p&gt;By default, Midjourney gives you a 1:1 aspect ratio. You can change this by adding the &lt;code&gt;--aspect 3:2&lt;/code&gt; parameter at the end of your prompt (or whatever ratio that you want to use, I've just added 3:2 here as an example).&lt;/p&gt;

&lt;p&gt;Now, let's fast-forward rather rapidly to yesterday evening. I feel like I've satisfied the question "can Midjourney be used to create content for web". So I decided to change it up a bit. I took some stills from one of my favourite films and used them as prompts to create variations in Midjourney v5. The results are astounding.&lt;/p&gt;

&lt;p&gt;Here's my first image prompt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2natdtb2id921v4ruv1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2natdtb2id921v4ruv1c.png" alt="Rick Deckard administering the Voight-Kampff test" width="780" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah, that's Rick Deckard setting up the Voight-Kampff before he questions Rachel in Tyrell's tower (guessed what the film is yet?).&lt;/p&gt;

&lt;p&gt;Using /imagine with the image as a prompt, a brief description, and the parameters for setting the version and image weight (at the time of writing, Midjourney uses version 4 by default, so we have to explicitly tell it to use version 5).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/imagine [imageurl] voight-kampf, blade runner --version 5 --iw 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was this beauty:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcrvjsrj6h8hhe40txxa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcrvjsrj6h8hhe40txxa.png" alt="Midjourney variants on the image prompt" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And of course, what kind of fan would I be if I didn't try to recreate this iconic scene...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexymzpi3eca9c2a8bwqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexymzpi3eca9c2a8bwqe.png" alt="Roy Batty holding dove" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll continue experimenting with what Midjourney v5 can do. So far, I am more than impressed. Maybe another article on this in a month or two where I share my new findings.&lt;/p&gt;

</description>
      <category>midjourney</category>
      <category>ai</category>
    </item>
    <item>
      <title>Running fixed-price projects in Agile</title>
      <dc:creator>David Johnson</dc:creator>
      <pubDate>Mon, 04 Jan 2021 18:05:31 +0000</pubDate>
      <link>https://dev.to/davidisnotnull/running-fixed-price-projects-in-agile-2n4o</link>
      <guid>https://dev.to/davidisnotnull/running-fixed-price-projects-in-agile-2n4o</guid>
      <description>&lt;p&gt;It’s easy to default back to a waterfall way of thinking when we’re running fixed-priced projects. As a business delivering a digital project, we need to capture the entire scope of the application to protect ourselves from scope creep later on. Because scope creep in a fixed-price project can get expensive, with a capital E.&lt;/p&gt;

&lt;p&gt;So, we start off doing our discovery work. Talking with the client. Investigating the systems that we need to extend or interface with, and reading any documentation that the client can provide us. Once we’ve done our discovery and got an understanding for what it is that the client wants us to build, we put together a software architecture document that we can present to the client and use as our statement of work. Right?&lt;/p&gt;

&lt;p&gt;Wrong.&lt;/p&gt;

&lt;p&gt;Let’s take a step back for a moment and think about the all important word in the title of this article - Agile. We may not be running a strictly Agile project, but we still need to be working inside the processes and frameworks that Agile teaches us as software developers.&lt;/p&gt;

&lt;p&gt;The first problem arises when we give a software architecture document to developers and expect them to use it to build out the features of our product. For us “older” developers who were around before Agile was a big deal, it might be ok. But a lot of developers who have grown up in Agile won’t be familiar with this - they’ll be used to planning sessions and tickets in Jira with acceptance criteria that they need to work towards.&lt;/p&gt;

&lt;p&gt;You’ve also got the issue that, as requirements change during the project’s lifetime (and they will) or new information comes to light, you’ll need to go back and update your document, then distribute a new version to the rest of the team, highlighting the elements that have changed.&lt;/p&gt;

&lt;p&gt;I understand completely that we need to have something to provide to clients that outlines what we’re going to be building, and how we’re going to do it, but that doesn’t mean that the initial starting point should be a software architecture document.&lt;/p&gt;

&lt;p&gt;If you have a development team, chances are you’ll be using some sort of board and ticket system to handle your feature delivery, be that Jira, Azure Devops, or something like Basecamp. So, why not leverage that to scope out your fixed price project before putting a statement of work together?&lt;/p&gt;

&lt;p&gt;There’ll be no difference when mapping out requirements in a fixed-priced or agile project. We still need User Stories, and the subsequent tasks that come out of this. My advice is start building your features in your board before you put together the statement of work. Involve your development team in the scoping out of these tickets as part of the discovery process (think of it as a longer sprint planning session). Get the input of the people who are going to be building the application - the more eyes on the scope at this stage of the game, the better prepared you’ll be when it comes to reducing risk later down the line.&lt;/p&gt;

&lt;p&gt;Your boards should always be your single source of truth on a project. If we have all of our stories broken down on the board, instead of tied up in an often unwieldy software architecture document, then when we get changes to requirements or new information come up later on, we can update the tickets with comments and attach the relevant documentation. This way, everyone knows where they stand.&lt;/p&gt;

&lt;p&gt;That’s not to say that you shouldn’t have a written statement of work - you need one to outline what you’re going to contractually provide. What I’m saying here is that it shouldn’t be the main source from which you’re working as you develop your application.&lt;/p&gt;

&lt;p&gt;Waterfall has gone out of fashion for a reason. If you’ve worked in it, particularly back in the late 90s or early 2000s, you’ll know why. It’s acceptable to use when your project is short, and when the application that you’re building isn’t overly complex. Yet you’re left running all of your tests at the end of the project, clients can’t really see anything whilst you're in the build stage, and you need to add in a lot of contingency to your timescales.&lt;/p&gt;

&lt;p&gt;The one thing I will say is that we should never confuse “fixed-price” with “waterfall”. You’ll have to follow a similar agenda, but we shouldn’t fall back to heavy documents and rigid phasing that are akin with the waterfall model.&lt;/p&gt;

&lt;p&gt;What you’ll need to ensure is that you have allocated a sufficient amount of time and resources into the discovery phase, because you’re going to have to capture as much as you can. You will miss things, and requirements will change. That’s ok. You just need to make sure that you include this in your contingency time.&lt;/p&gt;

&lt;p&gt;As I said before, get your development team involved early, and make sure that you run your UX and designs past them so that they can call out anything that might look ok in design, but that will turn out to be much more technically complicated than you’ll have initially thought.&lt;/p&gt;

&lt;p&gt;Start scoping out on your boards, and use these as a single source of truth. Build your statement of work off the back of the tickets you put together in your project board. Personally, I would forgo with putting together a software architecture document - you should have everything the developers need in your tickets, and you won’t need to use it as part of your statement of work because this document should not be that technical.&lt;/p&gt;

&lt;p&gt;Add contingency. I cannot stress how important it is that you don’t underestimate to win a bid. It will bite you in the ass hard if you do, and potentially damage your relationship with your client when the product that you promised will be done in12 weeks has crept into week 16.&lt;/p&gt;

&lt;p&gt;Phase your features. Just because you're running fixed-price doesn't mean that you can't complete features and demo them in iterative cycles, as you would do with Agile sprints. This means you can get the client involved early and show off your progress.&lt;/p&gt;

&lt;p&gt;If they change their minds about how something should work when they see it in anger. then you're capturing this mid-project rather than leaving all of their feedback to the end. And if they're adamant that it has to change to something that's outside of the agreed scope, then that's ok too - you can raise it as a change request, and negotiate additional time and cost for the project to cater for these changes.&lt;/p&gt;

&lt;p&gt;All in all, never forget the tried and proven Agile practises just because you're delivering something where you're defining the entireity of the scope at the beginning. You just have to adapt it to make it work, as you should be doing for all of your Agile projects.&lt;/p&gt;

</description>
      <category>projectmanagement</category>
    </item>
  </channel>
</rss>
