Thursday, December 25, 2025

ChatGPT Says: "Most Likely to Dockerize His Life"

Most Likely to Dockerize His Life

A lighthearted award — and a serious reflection on how I build systems.

My Your Year with ChatGPT recap came with an unexpected (but accurate) distinction: “Most Likely to Dockerize His Life.”

It made me laugh — and then it made me nod — because while the phrasing is playful, the idea behind it captures something fundamental about how I approach software engineering, automation, and even problem-solving in general.

Why Docker Shows Up Everywhere I Work

I don’t use Docker because it’s trendy. I use it because it enforces discipline. Containers create clear boundaries, explicit dependencies, and repeatable outcomes. They remove ambiguity — and ambiguity is where most projects quietly fail.

When I “Dockerize” something, what I’m really doing is answering a few important questions:

  • What does this system actually depend on?
  • How can someone else run it without tribal knowledge?
  • What needs to be versioned to keep this reliable six months from now?
  • How do we eliminate environment-based surprises?

Whether it’s a full-stack application, a background worker, a database, or a development toolchain, Docker helps turn those answers into something concrete and enforceable.

“Dockerizing My Life” (Metaphorically)

The joke lands because the mindset extends beyond containers. I naturally gravitate toward systems that are explicit, automated, and reproducible.

In practice, that means:

  • Clear setup instructions instead of “just install this one thing”
  • Scripts over manual steps
  • Documentation that reflects reality, not intentions
  • Workflows that can be rerun without guesswork

If something breaks, I want to know why. If something works, I want it to work again — exactly the same way.

Where AI Fits Into This Picture

AI has become a powerful complement to this way of working — not as a replacement for engineering judgment, but as an accelerator for thinking, planning, and iteration.

I use AI to:

  • Explore architectural options and tradeoffs
  • Generate and refine documentation and checklists
  • Scaffold code and identify edge cases
  • Design and improve automated workflows
  • Reduce friction in repetitive or error-prone tasks

Combined with Docker and automation, AI becomes part of a larger system: one that helps ideas move faster from concept to something runnable, testable, and deployable.

The Real Goal: Predictability

At the heart of it all, “Dockerizing my life” is really about predictability.

Predictable builds. Predictable deployments. Predictable workflows. Predictable outcomes.

When systems behave consistently, teams can focus on solving real problems instead of fighting their tools. That’s true in production environments — and it’s surprisingly true in everyday work habits as well.

Closing Thoughts

I’ll happily accept the award.

If being “Most Likely to Dockerize His Life” means building software that’s structured, automated, understandable, and resilient — then it’s a label I’m comfortable owning.

Because in the end, good engineering isn’t about cleverness. It’s about creating systems that work — today, tomorrow, and long after the novelty wears off.


Originally inspired by my Your Year with ChatGPT recap.

Friday, February 28, 2025

How To Use Vite Build Artifacts in Plain PHP Pages

Introduction

As a developer, I have had the opportunity to migrate several ordinary procedural PHP page websites into Laravel, a popular PHP framework. One of the key benefits of using Laravel is its seamless integration with Vite, a modern development server and build tool. Vite helps to build and manage CSS and JavaScript artifacts, making it easier to develop and maintain large web applications.

However, when migrating a website, it's often desirable to do so in stages, which can lead to a dilemma about how to manage CSS and JS artifacts between the new and old code. This can be further complicated by the fact that most older code runs JavaScript in the global scope, whereas Vite artifacts are generally used in the module scope. In this blog post, we'll explore how to bridge the gap between these two different approaches and leverage the advantages of using NPM to manage JavaScript versions and Vite to process CSS and JavaScript.

Understanding Global and Module Scopes in JavaScript

When including JavaScript on a page, it's important to understand the difference between loading a script normally versus as an ES module, since each behaves differently in terms of scope and import/export capabilities. Let's take a look at an example. In the global scope, variables and functions are attached to the global object (window in browsers):

/* Global scope */
var globalVariable = 'Hello, world!';
console.log(globalVariable); /* Output: Hello, world! */

In contrast, variables and functions defined in the module scope are not attached to the global object:

/* Module scope */
const moduleVariable = 'Hello, world!';
console.log(moduleVariable); /* Output: Hello, world! */
console.log(window.moduleVariable); /* Output: undefined */

The module scope provides a way to encapsulate variables and functions and prevent them from polluting the global namespace.

Considerations:

  • Global scope (classic scripts): Scripts loaded without type="module" run in the global scope. Any top-level variables or functions they define become properties of the window object.
  • Module scope (ES modules): Scripts loaded with type="module" use the JavaScript ES module system. Each module has its own scope, so top-level variables and functions do not automatically become global.
  • Load and execution order: Classic scripts execute in the order they appear. Module scripts are deferred and execute after HTML parsing.
  • Mixing modules and globals: If you include both, module scripts will not automatically see variables from global scripts unless explicitly referenced.

Using Vite Artifacts in Plain PHP Pages

So, how can we use Vite artifacts in plain PHP pages, while still retaining the ability to run JavaScript in the global scope? The answer lies in using the viteStaticCopy plugin to copy the raw JavaScript file managed by NPM into the folder of our choice with the name of our choice.

Install the static copy plugin using NPM:

npm install vite-plugin-static-copy

Here's a complete working Vite configuration, including the viteStaticCopy plugin in our vite.config.js file:

import {defineConfig} from "vite";
import react from "@vitejs/plugin-react";
import laravel from "laravel-vite-plugin";
import {glob} from "glob";
import basicSsl from "@vitejs/plugin-basic-ssl";
import inject from "@rollup/plugin-inject";
import {viteStaticCopy} from 'vite-plugin-static-copy';

/* Have Vite process all these resources */
let inputs = glob.sync(`resources/css/**/*.css`, {nodir: true});
inputs.push(...glob.sync(`resources/scss/**/*.scss`, {nodir: true}));
inputs.push(...glob.sync(`resources/js/**/*.js`, {nodir: true}));
inputs.push(...glob.sync(`resources/js/**/*.jsx`, {nodir: true}));
inputs.push(...glob.sync(`resources/js/**/*.ts`, {nodir: true}));
inputs.push(...glob.sync(`resources/js/**/*.tsx`, {nodir: true}));

export default defineConfig({
    plugins: [
        basicSsl(),
        inject({
            $: "jquery",
            jQuery: "jquery",
        }),
        react(),
        laravel({
            input: inputs,
            refresh: true,
        }),
        viteStaticCopy({
            targets: [
                {
                    src: 'node_modules/jquery/dist/jquery.js',
                    dest: 'assets/vendor',
                    rename: 'jquery.js'
                }
            ]
        })
    ],
    resolve: {
        alias: {
            "@": "/resources/js",
        },
        extensions: [".js", ".ts", ".jsx", ".tsx"],
    },
    build: {
        sourcemap: true,
        rollupOptions: {
            output: {
                entryFileNames: "assets/[name].[hash].js",
                chunkFileNames: "assets/[name].[hash].js",
                assetFileNames: (assetInfo) => {
                    /* Preload fonts only if they are woff or woff2 */
                    if (assetInfo.extname === ".woff2" || assetInfo.extname === ".woff") {
                        return "fonts/[name].[hash][extname]";
                    }
                    return "assets/[name].[hash][extname]";
                },
            },
        },
    },
});

The viteStaticCopy(targets) configuration will copy the jQuery library from the node_modules directory into the assets/vendor directory and rename it to jquery.js. Note that you can choose any destination folder and file name you like.

Run npm run build to generate assets.

We can then use this library in our plain PHP pages, while still retaining the ability to run JavaScript in the global scope:

<?php
const BUILD_PATH = '/build/';

$manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true);

$appCss = BUILD_PATH . $manifest['resources/scss/app.scss']['file'];
$appJs = BUILD_PATH . $manifest['resources/js/app.js']['file'];
$jqueryJs = BUILD_PATH . '/assets/vendor/jquery.js' ;
?>

<!-- The processed CSS file can be linked like any other CSS file -->
<link href="<?= $appCss ?>" rel="stylesheet" type="text/css"/>
<!-- The module type is required if you have used any imports in app.js, which will usually be the case -->
<script type="module" src="<?= $appJs ?>"></script>
<!-- With any un-processed JavaScript files, you can use the normal global scope -->
<script type="text/javascript" src="<?= $jqueryJs ?>"></script>

<script type="module">
    <!-- This uses the jQuery include in the app.js file processed by Vite -->
    $('body').prepend("<h1>Hi!</h1>");
</script>
<script type="text/javascript">
    <!-- This uses the jQuery from the the raw jquery.js file loaded into the global scope -->
    $(function() {
        $('body').append("<h1>Bye!</h1>");
    });
</script>

In this example, we're using the viteStaticCopy plugin to copy the jQuery library into the assets/vendor directory, and then using it in our plain PHP page, while still using Vite to build and manage our CSS and JavaScript artifacts.

Conclusion

In conclusion, using Vite artifacts in plain PHP pages can be a challenge, especially when it comes to managing CSS and JavaScript artifacts between old and new code. However, by leveraging the advantages of using NPM to manage JavaScript versions and Vite to process CSS and JavaScript, we can bridge the gap between these two different approaches.

By understanding the difference between global and module scopes in JavaScript, we can use Vite artifacts in plain PHP pages, while still retaining the ability to run JavaScript in the global scope. The viteStaticCopy plugin provides a way to copy raw JavaScript files managed by NPM into the folder of our choice with the name of our choice, making it easier to use Vite artifacts in plain PHP pages.

I hope this blog post has provided a helpful guide on how to use Vite artifacts in plain PHP pages. By following these steps, you can leverage the advantages of using Vite and NPM to manage your CSS and JavaScript artifacts, while still supporting older code that runs in the global scope.