Author: jamison

  • PHP is Alive, Well, and Worth Learning

    According to a new survey whose results were summarized in a recent Gizmodo article, CEOs are planning on slashing junior roles in the near future to focus on mid-level roles as work on low-level, simple tasks shifts to AI. Juniors are being told to study Javascript, Typescript, and even Java and Kotlin, emerging from their flowy robes and fancy cap clutching a C.S. degree in these topics, and then being told they don’t have a job because AI can do it. To these juniors, I have a rallying cry that is equal parts fervent and encouraging: learn PHP!

    My friend Mike Page gave a great UnCon talk recently at PHPTek 2026 about how pervasive and trope-y the idea of PHP being a dead language has become. There are countless memes about it, from two-headed hammers to chronologies of “it’s <year>, PHP is out, learn <lang>”. I am here to state, unequivocally, and I especially hope you hear me if you are a junior engineer worrying about your burgeoning career:

    The rumors of PHP’s death have been greatly exaggerated.

    me and a metric crapload of other paid professional PHP developers

    At the same conference I mentioned above, I stepped into an impromptu job fair to support a friend. In that job fair, I heard no fewer than three companies mention that they were looking to hire for a role supporting a “port old PHP to new PHP” role. The “from” codebases ranged from CodeIgniter 3 to older versions of Laravel. The “new” codebases included Symfony, Laravel, CakePHP, and possibly more. Not a single “we’re escaping PHP for Node” or “We’re rebuilding our legacy backoffice in React from PHP5”. No, from PHP to PHP. And, despite the name, this was a Javascript con, too! Indeed at the end of 30 minutes I realized that there were quite a few React- and Vue-first developers in the room with us. In speaking to some of them afterwards, I found out that it’s not just PHP developers that are seeking to learn PHP. The language has an immense amount of pull and gravitas, once you look past the memes.

    The famous ‘PHP is a hammer with two heads’ meme. The joke landed in 2012.

    An additional reason I hear cited all-too-often for avoiding PHP is that it’s old. If your argument is “this language is garbage because it was created in the early 90s” then, first, I shall challenge you to a duel as that is when yours truly was born–and indeed I have aged like a Lagavulin–and second, I would ask if you had you seen the most recent PHP release? It was November 2025. PHP is a language that is still going strong after 32 years. Somebody even got PHP version 1 from 1995 working and responding to HTTP requests. In addition to the PHP language itself, most of the popular PHP frameworks place an extremely high value on backwards-compatibility (see Symfony framework’s Backwards Compatibility Promise).

    Something important to keep in mind: “it works” is not the same thing as “good code”. There are dozens of features that are present in more modern versions of the PHP language that older versions can only dream of. But if you need to write something once and have it work forever, PHP is a safe bet.

    Shift your attention now to framework-of-the-week land. In my career I’ve had the dubious privilege of maintaining a 5+ year old React application. Opening the Dev Console in my browser was complete carnage: deprecations, warnings, errors…ketchup and mustard everywhere making it impossible to locate and fix the actual issues my users were reporting, like componentWillMount has been renamed, and is not recommended for use, or the same for componentWillReceiveProps, or even this fun block of deprecated dependencies that would require weeks to fully refactor:

    That codebase became “don’t touch it unless you HAVE to” very quickly. AngularJS was gaining some steam as one of the first SPA frameworks when Google rewrote it from scratch and shipped it as a different framework that just happened to share the name. But, if you built on AngularJS 1, you didn’t get an upgrade path, you got an EOL notice. What if, in that time, you built a projection-ready app that needed to run for 20 years? This is not uncommon in the government and educational space. I have never heard of this happening with the PHP language itself. Frameworks? Sure, but not the core.

    Why does one class of languages rot and the other doesn’t? For PHP, backwards compatibility is a language mandate. Don’t believe me? Check the RFCs, see how many are rejected for not considering X or Y edge case that would adversely impact users of older versions of the language. The syntax for attributes (#[MyAttrib]) was in no small part decided because the ‘#’ symbol is a line-comment in older versions of PHP, meaning that versions of the language that weren’t compatible with that syntax would simply ignore it. I greatly admire the folks that are submitting RFCs to the mailing list, because so much thought goes into every little detail. That community will not let a backwards-incompatible comma get through. Meanwhile, it seems all-too-common that Javascript frameworks need transpilers and reinvent themselves biannually, leaving the carcasses of their code on your hard drive to clog up your technical debt queue.

    I greatly admire the folks that are submitting [PHP] RFCs…that community will not let a backwards-incompatible comma get through.


    Aside from stability, what is so great about PHP that everyone is missing out on? SO, so much, and this is the part of this blog that I was the most excited about writing. At my current job, I have the great opportunity to work on a bleeding-edge API: latest PHP version, latest features, latest dependencies. If it’s stable, actively maintained, and fits one of our use cases, then it’s a candidate for use. Here are some of my favorite PHP features that you may not be aware of:

    • Attributes – attach metadata to your classes and methods to add yet another solution for sharing code between unrelated parts of the codebase. I used this to build attribute-based routing which helped me clean up a 200+ line config file that was a manual update chore.
    • Reflection – scan your PHP files to get information about the AST without building a tree parser. I did that once, in Roslyn for C#, it was…ok, it was actually kinda fun…but not something I would want to do again. Reflection and attributes go together hand-in-hand.
    • Enums – represent magic strings and other primitives in your app as an actual typed object. Loosen the coupling between the values in your database and what is read in your code. I’ve used enums to sidestep complex issues in apps with big databases that would have required a risky query otherwise.
    • Tooling like PHPStan and Rector. The former is what I call a “bug preventor”…it prevents bugs before they happen. The latter is an engine for rapidly applying boring refactorings in a methodical way, saving you literally dozens of hours of manual edits and embarrassing reverts caused by AI mishaps.
    • Mutation testing with Pest. You’ve got tests, bravo, but how good are they? Pest will modify small bits of your SUT to see if your tests catch them. If testing is more than a casual nice-to-have in your project (and it should be), you should absolutely try mutation testing with Pest.

    These are just a handful of basic features, there are even more advanced ones that even I haven’t played with yet, like fibers (pause/resume live code) and async (coroutines).

    That’s nice, but, I’m a junior dev that just graduated and I don’t even know what half of these things are, except maybe async. We didn’t cover this stuff in my CS labs! Why should I care?

    You should care because features like these are what turn a regular-old-boring PHP app into an enterprise-grade powerhouse, and that’s where the money is. Ever wondered how to write scalable code? This is how. Ex-FAANG folks I’ve talked to tell me these are exactly the patterns they expected new hires to bring on day one.

    Here’s the PHP advantage: these are language-level features, documented in one place, and guaranteed to be backwards compatible across major versions. In JS land, the same patterns live in framework documentation, and they’ll be subtly different from React, to Vue, to Redux, to Angular 5/6/7/8/9/…/21, or whichever new framework is the flavor of the week.

    And, *grabs soapbox and stands upon it*, here’s my issue with FAANG, by the way. They are the ones who have caused this mess of creating and proliferating so many JS frameworks that it’s impossible to know which one is right to learn, then abandoning them in service of their latest business venture. (Seriously, go to killedbygoogle.com and search for ‘javascript’.) FAANG churn is a huge part of why JS is the way it is.

    Conversely, PHP is stewarded by The PHP Foundation and is not owned by anyone. So it can’t be killed by anyone. Now, as fun as it is to dunk on Big Tech, I will say that their commitment to advancing computer science is admirable. But how they’ve done it, I vehemently disagree with, and I’m not the only one. FYI, I’m not an embittered, rejected job applicant: I’ve never applied to work for them, and I never would. If I were to exit the PHP ecosystem, I would go for a C++ or Python-focused team. Why? Because those are two other languages that aren’t owned by a corporation and therefore can’t be killed off.


    So, juniors, I sincerely hope that your transition from higher education to the corporate world is smooth and easy, but if it isn’t, here is my advice:

    1. Learn concepts first, and languages second. Once you’ve learned the concepts you can learn any language. And, don’t worry about picking a framework at first, either. Frameworks come and go, languages last for as long as they are maintained, but concepts live forever.
    2. That said, if you have to pick a language to learn, pick PHP. IMO it is extremely easy to learn and understand because it makes so many powerful features easily accessible natively, and even more functionality (Redis, MySQL, advanced debugging) is one extension-install away.
    3. Here’s the most important bit: if you already have Javascript experience, your time was not wasted. I have yet to work in a PHP position that didn’t require some Javascript knowledge. Your experience is additive, not detrimental. On your resume, I would put “Javascript”, though, rather than <FrameworkOfTheWeek>

    Myself and millions of other PHP developers know what the rest of the tech world seems to not know: PHP is alive and well, and there is a bevy of work to go around. Maybe not at huge multinational companies, but at your local mom-and-pop shop or small business. Happiness with your work and a stable paycheck matter more than the names on your resume, I have lived it both ways. In a few years, or maybe less, when the AI bubble “pops” and there are massive volumes of crap slop code to clean up, remember your old friend PHP, and the companies I mentioned earlier looking to port old code TO PHP, not FROM PHP. Help mop up the AI slop and replace it with nice, clean PHP.

  • PHP Doesn’t Have a Marketing Problem (A Response to Brent Roose)

    PHP Doesn’t Have a Marketing Problem (A Response to Brent Roose)

    I was reading the latest newsletter by Brent Roose, Developer Advocate for PHP at JetBrains, and I found myself disagreeing — out loud, apparently, because my 6-year-old looked over from her morning routine, unmoved, most likely because she is not a PHP developer. The premise is that PHP’s biggest problem is marketing, and that the PHP Foundation should be paying for a website redesign, a full-time docs hire, conference speakers, an omnichannel social media presence, internals blogging/vlogging, and “high-impact features” that can be marketed.

    I have a ton of respect for Brent, have interacted with him on his streams, and he has done a ton for developers in the PHP world, so I want to be clear up front: I am not interested in dunking on him. I read most everything he writes and emphatically issue “+1″s in my head. I was (and am) genuinely interested in his projects such as Tempest, and I think his framing of the problem is at least worth taking seriously — even if I think he’s wrong about the answer.

    But, I DO think he’s wrong, and pretty fundamentally so. Let’s take it point by point.

    Re: paying a design agency to redesign php.net

    Brent’s first suggestion is that php.net needs a proper design-agency-led redesign. I would push back on the premise harder than the suggestion, per se: I don’t actually think php.net looks dated, I think it looks timeless. I don’t think too many people coming to PHP from Node, Go, or Rust are bouncing because the homepage is too gray…er, purple. The Foundation just ran a paid design contest for the 8.5 release page in November, with $1,500+ in prizes. This shows the community is already interested in iterating on design, just incrementally and on volunteer-friendly terms instead of via an agency engagement.

    The site is plain, yes. It’s fast. There’s no cookie banner, no AI-generated hero illustration, no parallax scroll, no autoplaying videos or GIFs, no sticky CTAs getting in the way. You land on it, find what you came for, and leave. It’s the opposite of sticky, like a quick-reference card instead of an audio-visual experience. In 2026, when seemingly EVERY. OTHER. SITE. has been redesigned by someone who clearly briefed an LLM to “make this look snazzy” resulting in it looking nearly identical to every other AI-built site (eugh)…I think php.net’s restraint is a feature, not a liability. Readability beats flashiness, especially for developer-facing resources.

    Here’s a more direct counter-example, too: Some CakePHP Core folks (shout out to Kevin and Jos) just spent a few weeks migrating our entire docs ecosystem from RST to VitePress. The new sites look great, didn’t change navigation patterns (so we didn’t confuse our existing users), and we did the whole thing on volunteer time. No design agency required. If a small framework’s core team can pull off a tasteful refresh on weekends, the language itself doesn’t need to outsource its identity to a branding consultancy.

    I’m not opposed to php.net evolving. I just don’t think hiring an agency is the move.

    Re: paying someone full-time to work on the docs

    This one is among the few of Brent’s points that confused me, because as far as I can tell, this is something the PHP Foundation already owns. Their team’s listed contributions explicitly include “writing documentation,” and as of their 2026 hiring round they fund eleven developers across the language.

    If the argument is “the Foundation should hire more docs-writers”, then that’s totally reasonable to advocate for! But that’s a different argument than “pay someone to do this” because someone is ostensibly already being paid to do this. I have at least one friend in my circle who is a PHP docs contributor. Are they being paid? If not, why not? There should be ample funds available, because the Foundation has a transparent funding pipeline: sponsors include JetBrains, Automattic, Sovereign Tech Agency, Laravel, GoDaddy, Sentry, Symfony, Passbolt, and many others.

    Sidebar: Brent works at JetBrains, which has been one of the Foundation’s largest sponsors since day one. I think there’s an interesting opportunity there for him to push from the inside.

    The other thing that nagged at me reading this section was the underlying claim that people can’t actually learn PHP effectively from the current docs. I just don’t think that’s true. The PHP docs are great. They’re not a hand-holdy tutorial site, and they shouldn’t be, because excellent supplemental tutorial sites like Refactoring.Guru already exist for that purpose (I link this site to my team ALL the time). If anything, the docs could use a little more crosslinking to those resources. That’s a much smaller intervention than “hire a full-time writer.”

    I also want to plug Tylae, an actual real-life PHP docs contributor I had the pleasure of meeting (virtually) recently. There are people doing this work now, on volunteer time, and I’d rather see Foundation resources go to people like them than to a brand-new contractor with no community context.

    Re: paying conference speakers to speak outside the PHP bubble

    This one I agree with, so I won’t dwell on it too much other than to say: I volunteer as tribute!! 😆

    Cross-pollination between communities is one of the most underrated drivers of language adoption. I speak from personal experience. One of the best, most entertaining, most useful talks I ever sat through at a PHP conference was a Lemon Lemon talk about Progressive Web Apps – highly orthogonal to PHP, delivered to a room full of PHP devs. Nobody in the room cared the slides weren’t about PHP. The ideas were good, and the delivery was sharp. I went home and built and deployed a PWA, just for the fun of it! Imagine if the roles were reversed, and a JS or Rust dev went home and developed a quick CakePHP plugin or PHP Agent Skill. Amazing.

    In an era where agents and MCP/A2A are taking the world by storm and everybody’s trying to figure out whether Node.js or Python is the best for LLM-enhancing scripts. Why not PHP, I ask you? Why not PHP?

    So sure, fund some PHP folks to go talk at JSConf, GopherCon, RustConf, whatever. I’d genuinely watch a “PHP for Skeptics” talk show up in front of a Node audience.

    Re: investing in social media everywhere

    This one I have some thoughts on. Brent’s list of platforms PHP “should be everywhere” on includes X, Reddit, newsletters, blogs, Instagram, TikTok, Facebook, and LinkedIn.

    Notably absent…Mastodon. As in, phpc.social, the most popular PHP-flavored federated server that I know of, where a huge chunk of the post-Twitter-eXodus PHP community currently hangs out. I’m not sure if Brent is on Mastodon, I couldn’t find them with a quick search, but if you’re reading this Brent and no one has yet done so, please allow me to warmly extend the invite! We’d love to have you!! The more the merrier. The point is: the Fediverse is where a lot of the actual human-oriented conversation is happening right now, especially the conversation that developer advocates want to be a part of.

    A few of the other platforms on the list just give me pause, and it could be because I’m an old-Internet denizen. X and Reddit are increasingly bot-saturated (“@grok is this true?” “@grok make a meme of this” smh.) and algorithmically hostile to non-engagement-bait content: PHP showing up there isn’t going to convert anyone, because (at least on my feed) it would appear between the “Anthropic just released the cheat code to using Claude to make ZILLIONS!” post and the flood of AI-generated fake imagery that makes me feel gross just scrolling past it. And what, exactly, would the PHP Foundation post on TikTok and Instagram? There are only so many cute pics of elePHPhants one can take. How about a short-form video of someone talking about their day as a core PHP developer? I just don’t think that one lands on anyone’s feed.

    Obligatory elePHPhant pic. Gifted to me by my friend John Congdon, of SDPHP

    I’m not saying it can’t be done on these social media platforms, and again I’m quite skeptical and cynical towards most social media for technological news, but the lift seems too high to make it worth it. Newsletters and blogs are kind of the same thing, structurally, and I actually think Brent is doing a great job with these already.

    What I’d actually advocate for…and this might just be a thinly-veiled “more of what already works” pitch…is leaning into the federated, blog-and-RSS, conference-and-podcast ecosystem that already exists. Because it does exist, and it’s good, and it doesn’t require anyone to make TikToks, and doesn’t require me to “like, comment, and subscribe” to anything.

    Re: killing the mailing list and replacing it with vlogs

    I was having this exact discussion with a coworker the other day, so my face went :O when I saw this show up in Brent’s newsletter. Internals being “inaccessible” because it’s on a mailing list is not really the problem, because most users don’t want to read the actual internals discussions. PHP is, deep down, very gnarly C code. Brain hurty, unless you live and breathe it. Most working PHP developers do not need or want to follow the day-to-day of zend_execute_data getting refactored.

    What people do want is something like Anthropic’s Claude Code changelog: a curated, regular, technical-but-readable summary of what’s shipping, what’s in progress, and what’s coming. Bullet points, hints, with links to the deeper RFC discussions for people who want to go further. I would subscribe to that in a heartbeat. That’s the artifact I think is missing — not a vlog series, and specifically because…

    Brent, you are doing this already, and doing it well. So is PHPUgly and PHP Architect. My friend John Congdon and his pals have been putting out quality, entertaining, perfect-vent-needed-for-a-weekday-afternoon podcast content for ages. Also, Roman Pronskiy is doing it on the Foundation side. Suggesting “someone should blog/vlog about PHP” without acknowledging the people currently doing this work read to me as inadvertently dismissive. But I’m hoping I misread it. There’s a thriving content layer around this language already. Maybe what’s missing isn’t more content, it’s better aggregation and discovery of the content that exists.

    What a logo. I mean REALLY! Somebody get these folks a Design Award!!!!

    Re: investing in high-impact features and marketing them

    My response to this is the same as to the docs point above, which is isn’t this what the Foundation is supposed to already be doing? Their funded developers ship features, fix bugs, and the Foundation publishes transparency reports periodically detailing all of it. If specific sponsors, and there are a lot of them, want particular high-impact features prioritized, then the channel for that conversation already exists. But if it’s not happening, then something is broken internally with the Foundation. If those conversations ARE happening and the rest of us aren’t hearing about them, that’s an information-flow problem, and it loops back into the “PHP Foundation Changelog” idea I floated above. That’s an intervention I’d actually advocate for.

    From the outside looking in it seems like all the tools are in-place. Granted, the Foundation is less than 5 years old at this point, so you could attribute this breakdown to their relative infancy. But, I fervently hope this gets figured out soon, else it could cause some of those sponsors to pull their funds. The shininess of philanthropy does wear off eventually (see the “past sponsors” list on the PHP Foundation’s website).

    OK, rant over. What am I actually arguing for?

    When I lay these rebuttals out next to each other (and, thank you for sticking with me while I pontificate my thoughts point-by-point) I notice they all share a shape: most of Brent’s suggestions are framed as “PHP needs to buy these things,” and most of my responses are some flavor of “this is already happening, but you may not be looking in the right place.” That’s a big distinction. It’s the difference between:

    1. this language has a marketing problem and
    2. this language has a visibility into its own community problem.

    If I had to name PHP’s biggest problem in one phrase, it wouldn’t be marketing. It would be fragmentation. Do the CakePHP developers know what the Laravel developers are doing? Do the xdebug developers know what the dd-trace-php developers are doing? And most importantly: do the people doing the work and the people consuming the language know about each other? The answer to that isn’t a JetBrains-scale marketing budget. It’s better aggregation, better cross-pollination, and more broadcasting of the volunteer and Foundation-level work that is already happening.

    Brent is uniquely positioned to help with this, by the way. He works at one of the Foundation’s top sponsors. He has a massive audience, and he’s already running a top-tier blog that I am delighted to see show up in my inbox, and a YouTube channel about PHP that is quite successful. If he wants more of what he described, he is maybe one of ten people in the world that can make it happen: so make it happen. This is the principal reason I wanted to write an almost 2,000 word response to it (and, as you can probably tell by my grammar, I do not use AI to write my blog posts).

    So, with respect and admiration, I disagree, Brent. PHP doesn’t have a marketing problem. It has a we should talk to each other more problem. And we’re already doing that, just not in the channels you may think.

    Further Reading

  • ChatGPT 4o Image Generation: Not Ready for Chess, Yet.

    ChatGPT 4o Image Generation: Not Ready for Chess, Yet.

    I was as excited as any AI-aware engineer would be when I learned that OpenAI had announced a grand leap forward in the image generation capabilities of it’s 4o model. I’ve been using ChatGPT’s image-generation capabilities for a while, mostly to generate funny profile pictures for my privacy-conscious online friends who didn’t want to share their real-life face, but I needed a way to identify them in my contacts regardless. Interestingly, though there are many models out there, I’ve never felt a compulsion to try any other than DALL-E and (now) 4o. No Midjourney for me.

    I read an interesting comment on Hacker News that describes the new image generation capabilities as “reasoning in pixel space” and used a Tic-Tac-Toe board as an example. Here is the exact text:

    Example: Ask it to draw a notepad with an empty tic-tac-toe, then tell it to make the first move, then you make a move, and so on.

    To me, this prompt is a little boring. I am an avid chess player, though, so I immediately thought of a more fun application: could ChatGPT 4o’s new image generation capabilities reliably render either a 3D or a 2D-model of a chessboard in a particular position, much like the T.T.T. example above? Fortunately, chess benefits from algebraic notation–a textual format for representing positions–something Tic-Tac-Toe woefully lacks (and why, a curious mind asks). I knew I wouldn’t be able to feed it a 20-move game like the ones I normally play so I kept it simple: The London Opening.

    Intro to London

    London is considered a very basic opening, because White plays pretty much the exact same opening moves regardless of what Black does. Bearing in mind the historically limited capabilities of AI image generation, I considered it an ideal candidate due to the simplicity and shortness. To be explicit, this is the series of moves I was hoping to render with 4o:

    1.d4 d5 2.Nf3 Nf6 3.Bf4

    This is what this position should look like on a real (2D) board, courtesy of Chess.com:

    I will leave the manifestation of a 3D board as an exercise to the reader. ChatGPT won’t be able to help you here, as I later found out.

    First Attempt

    Prompt 1

    Here is the first prompt I used and the outcome:

    Generate an image of a 3-dimensional chessboard displaying the London Opening, characterized by this algebraic notation:

    1.d4 d5 2.Nf3 Nf6 3.Bf4

    No humans need be present in the picture, only the chessboard in the CORRECT orientation, the pieces in the CORRECT position, and it can be sitting on a simple table with a simple background. The focus is on the board.

    Result 1

    For a zero-shot, and my admittedly less-than-stellar prompt (I was running a quick experiment, checkmate me) this isn’t bad. While you can tell there are some low-level issues like off-center pieces, incorrect rank/file labeling, and a really silly-looking knight on g1, the key problems are with the pieces that ARE on the board. I described the multitude of problems in a follow-up prompt.

    Second Attempt

    Prompt 2

    In this prompt I used a continuance to explain what was wrong with the generated image, focusing on the extra knight, the missing pawns, the missing Queen, and the overall wrongness of the pictured position based on the input:

    Close but not quite there. You got the style right, but the position is wrong in several ways:

    1. There are three black knights on the board.

    2. The black knight on f4 is two squares downfile of where it should be. I think the white bishop should be there.

    3. There is no white queen on the board

    4. I do not see the pawns in the center.

    Try again

    The purpose behind this was to provide slight corrections to the LLM model in the hopes that the second generation would be as high-quality as the first, but with the key improvements to bring it closer to the ideal final state. I had a feeling by this point already however that the task I was asking for was above the capabilities of the model in its current state. Still, I kept digging.

    Result 2

    Interestingly, our extra knight disappeared, but so too did other critical pieces: the white Queen is still missing, a white knight is now missing, and several pawns are missing from both sides of the board. But, the white bishop is a lot closer to the correct square than it was before, the only problem is it is currently sitting where the white knight should go. I do appreciate that the d2 pawn is missing, because it almost looks like the model moved the pawn out of the way to get the bishop to its intended square, which makes more sense than an image depicting a bishop that has “jumped” a blocking pawn, a move which is not legal in chess. However, it appears to have handled this issue by “disappearing” the pawn entirely. Gotcha!

    Third Attempt

    Prompt 3

    It was at this point that I decided that my human-written prompts weren’t quite cutting it (simplistic as they were). So, I used a different 4o chat to create an AI-engineered prompt, as follows [note: ChatGPT fluff removed for brevity].

    Better, but still not quite there.

    I will try telling you the exact piece layout to see if that helps you.

    Below I will express the board as an 8×8 matrix indexed from a1 to h8 (bottom-left to top-right from White’s perspective), using piece abbreviations:

    • Capital = White, lowercase = Black

    • P = Pawn

    • N = Knight

    • B = Bishop

    • R = Rook

    • Q = Queen

    • K = King

    • . = Empty square

    Here’s the coordinate layout (from rank 8 to rank 1):

    8 | r n b q k b n r

    7 | p p p . p p p p

    6 | . . . . . . . .

    5 | . . . p . . . .

    4 | . . . . B . . .

    3 | . . . . P N . .

    2 | P P P P . P P P

    1 | R N B Q K . . R

        a b c d e f g h

    Now, please try again

    Result 3

    In this image, we can see that the model’s generation capabilities kind of…fall off of a cliff. The rank and file labels are way off, even mixing up letters and numbers on the right side of the board. Also, white’s kingside rook has been replaced with a pawn, and…actually…*stares closer*…there is no white king on the board. Yay! ChatGPT just invented a foolproof way to win a chess game as black! Just generate the board already in a checkmated position!

    Sarcasm aside, this is likely due to the extended context of the chat, which has now gone through three rounds of generation. One of the biggest drawbacks of AI is that as the context window lengthens, the responses typically get worse or less detailed. An example of this is an AI-led project I did recently to convert recipes from audio transcriptions to HTML. Initially, the model was programmed to “always” include the amounts of each ingredient, and infer them from general cooking/baking knowledge if they were missing (transcription garbled). After converting 50+ recipes, all quantities were completely gone, though the ingredients were still present. A reminder in a new prompt was required to fix the issue.

    However, the “reinforcement of the rules” trick (it probably has a stuffy official name, admittedly I don’t know it) doesn’t appear to work with image generation.

    Final Attempt

    I decided to give it one last shot before calling it a day, still (perhaps erroneously) believing that it was the prompt that was the problem, and not the model. Granted, this image generation is lightyears ahead of what we had before, where most text was either only partially legible or complete gibberish, as well as other obvious artifacts of computer generation like hands with too many fingers or other small details that are missed or fudged.

    Prompt 4

    This was another “LLM optimized” notation generated by 4o for 4o: I’m not convinced it is any better than any of the previous attempts.

    No, some pieces are still not correct. Here is another way of expressing the notation in text format, please generate an image EXACTLY according to the below piece arrangement:

    Rank 8: [Black Rook on a8] [Black Knight on b8] [Black Bishop on c8] [Black Queen on d8] [Black King on e8] [Black Bishop on f8] [Black Knight on g8] [Black Rook on h8]  

    Rank 7: [Black Pawn on a7] [Black Pawn on b7] [Black Pawn on c7] [Empty on d7] [Black Pawn on e7] [Black Pawn on f7] [Black Pawn on g7] [Black Pawn on h7]  

    Rank 6: [Empty on a6] [Empty on b6] [Empty on c6] [Empty on d6] [Empty on e6] [Empty on f6] [Empty on g6] [Empty on h6]  

    Rank 5: [Empty on a5] [Empty on b5] [Empty on c5] [Black Pawn on d5] [Empty on e5] [Empty on f5] [Empty on g5] [Empty on h5]  

    Rank 4: [Empty on a4] [Empty on b4] [Empty on c4] [White Pawn on d4] [White Bishop on e4] [Empty on f4] [Empty on g4] [Empty on h4]  

    Rank 3: [Empty on a3] [Empty on b3] [Empty on c3] [Empty on d3] [White Pawn on e3] [White Knight on f3] [Empty on g3] [Empty on h3]  

    Rank 2: [White Pawn on a2] [White Pawn on b2] [White Pawn on c2] [White Pawn on d2] [Empty on e2] [White Pawn on f2] [White Pawn on g2] [White Pawn on h2]  

    Rank 1: [White Rook on a1] [White Knight on b1] [White Bishop on c1] [White Queen on d1] [White King on e1] [Empty on f1] [Empty on g1] [White Rook on h1]

    Result 4

    I would write a paragraph with some analysis of the model’s output in this pass, but it seems unnecessary given that the generated image is so similar to the ones generated by previous prompts, but with even more mistakes. Too many to correct in a follow-up prompt without spoiling the original context chain, in my opinion. One additional note on the similarity of the images: it is so noticeable, I wondered if OpenAI may be using some sort of caching of graphical data behind-the-scenes to save on processing power.

    After four passes, I think it’s reasonable to conclude that as of March 2025, when this article was written and published, 4o is not QUITE capable of rendering an image depicting a scenario as complicated as a chessboard in a particular position. As a follow-up experiment, I think it would be interesting to see if it could generate an accurate endgame position, with far fewer pieces on the board. Perhaps the reduced complexity, combined with those “LLM optimized” board layouts (what’s wrong with algebraic notation, ChatGPT??) some interesting results could develop.

    Conclusion

    I’ve been using AI and AI image generation for over a year now, like most techies. Obviously, it’s become mainstream in the ensuing time, the point where family members are reaching out to me to determine if I’ve heard of “Claude” or “Chat Gee Pee Tee”. The answer I give them is the same:

    1. I use these tools daily, and I use multiples of them for a variety of purposes.
    2. AI is the next technological revolution. Get on this train while you can.
    3. If you don’t know how to use AI, ask it. (Unless you’re a chessmaster, in which case I would wait until the next model update.)

    One fun fact to close: Google’s Gemini is another model that is fairly good at image generation, but it has no problem generating images of copyrighted figures. Here’s one I made for my daughter, a professed Princess Peach aficionado:

    Further Reading

  • My five-step process for eliminating technical debt in your (big) projects

    I was conversing with a colleague today and we were lamenting the ever-growing pile of technical debt in a project we were both working on. Technical debt is not a new concept in software, in fact it’s probably one of the oldest, but what’s less discussed (I feel) is how to solve it. It’s simple enough to add // KLUDGE or // FIXME comments to your code and address them later on, or wait for a more senior engineer to get un-busy enough to help you out. But, this is an untenable solution on larger projects with a veritable mountain of technical debt that has built up over the years.

    I’ve spent some time thinking about this recently and I’ve come up with a framework that makes sense in the context of 1) large applications that are 2) under active development and are 3) uptime-critical that 4) have a very long backlog of technical debt that is actively hampering ongoing development efforts. And, as if there weren’t already enough acronyms in this industry, I decided to create one more: I call this framework PURGE.

    • P – Prevent It
    • U – Understand It
    • R – Review It
    • G – Group & Grade It
    • E – Eliminate It

    In the following sections I will briefly discuss each aspect of the framework, and then at the end I’ll explain why I think it’s especially effective for larger projects.

    Prevent It

    An ounce of prevention is worth a pound of cure, it’s a phrase I’ve heard many times throughout my life but surprisingly rarely in the context of software development (probably because SWEs usually use the metric system, lol). The adage rings true here, though: the best way to address technical debt is to prevent it from happening in the first place. This is not always possible, of course, and in fact I personally am comfortable with about a 5% technical debt percentage in code that I produce, so long as it’s not security- or privacy-critical code.

    Depending on your language and/or stack (mine is PHP, as many of you know) there are many options for preventing technical debt from occurring, but they broadly fall into three categories:

    1. Code Linters like PHP_CodeSniffer (AKA phpcs)
    2. Static Analyzers like PHPStan and Psalm
    3. High-Quality Code Reviews

    These tools are (in my opinion) very easy to integrate with any project that uses Composer, no matter your PHP version. And, with Continuous Integration (CI) pipelines being at an all-time high for popularity, there really is no reason not to run these tools on every commit/push of your project. Coding with these tools may sometimes prove cumbersome, but they absolutely do prevent bugs and technical debt from accruing when you’re not looking.

    And, if you’re not already doing thorough code reviews in 2023, let me quote from this article I found on paddle.com:

    One of the easiest ways to identify technical debt is to review the code. You can look for code smells or bad coding practices that can lead to maintenance issues in the future. Code smells are indicators that the code may have a design problem or may be difficult to maintain.

    What is a code smell? A “code smell” is a term used to describe any characteristic of the code might have issues that could lead to problems in the future. Some common examples of code smells include overly complex code, duplicated code, long methods or functions, and excessive use of comments. Code smells are often subjective, and what one developer considers a code smell may not be considered a problem by another developer.

    From “The SaaS leader’s guide to technical debt: business impact + how to identify and reduce it” on paddle.com

    Understand It

    As mentioned previously, technical debt as a whole is unavoidable. Sometimes when coding, we don’t know what we don’t know and so we make decisions that we later have to pay for. However, I think it’s important to review these decisions on a regular basis so that we can understand where the technical debt comes from. This is especially important for avoiding the Sunk-Cost Fallacy that I have seen far too many junior engineers and product managers commit.

    Use your brain to bail yourself out. Refuse to sink. Everyone will love your attitude.
    Photo by Pok Rie on Pexels.com

    When doing this exercise, I like to utilize the 5 Whys Exercise (straight from Sakichi Toyoda’s playbook). I find this really helps get to the root of the problem. Here are some sample questions one might ask when evaluating the first of the five why’s behind an instance of technical debt:

    • “Why did we not research open-source solutions before writing this component in-house?”
    • “Why did we not search our own codebase before re-inventing this wheel?”
    • “Why did we not document that algorithm X should be done the Y way and not the Z way?”

    It’s incredibly important not to place blame during these discussions. Some people call these “blameless retrospectives” and I like that attitude. Everyone, from juniors to seniors to principals, makes mistakes and introduces defects and debt sometimes. There is no place for ego here.

    Review It

    One of the biggest “cringe moments” I always encounter on big projects with a lot of technical debt (and this finger is pointed squarely at my own chest, do not fret) is when tech debt gets added to a list where it essentially goes to die. You might call this the “One Day List” (as in, we’ll do it one day) or the “Profitable List” (we’ll do it when we’re profitable) but you should really call it what it is: sweeping it under the rug. Yikes. If you make a list of technical debt and then never review it, it might as well not exist.

    Hiding under those undulating threads are those 10,000 JIRA tickets you’re ignoring
    Photo by Isabelle Taylor on Pexels.com

    A colleague pointed out to me once that the biggest reason why technical debt accrues is not lack of desire to address it, or the push to go to market (well, maybe that one a little bit), but rather a lack of visibility into the growing balance sheet of tech debt. It’s super easy to say “we’ll deal with that later” when you know later is never going to come. It speaks to a lack of accountability. But, what many people forget, again myself solidly included, is that there is always a price to pay with these things. It may come three months from now, or it may come when you’re nearing your IPO (looking at you, reddit with your terrible UIs).

    Don’t just make lists, review them on a regular basis with your entire team. Then, and only then, can everyone start getting a handle on what needs to be done.

    Group & Grade It

    So, you’ve got your list and you’ve (perhaps miraculously) gotten the Sales department to hold off on some new feature requests for a period of time so as to allow you to address some of your debt. How do you start? I recommend a two-step approach:

    1. GROUP your codebase into major areas where debt is concentrated
    2. GRADE each area by the severity of the technical debt contained within

    These groupings might be along the natural “fault lines” of your code, e.g. the Models vs. the Controllers, or they may be more along the lines of features e.g. “User Login Debt” vs. “Custom Reporting Debt”. The groupings may be more subtle still, but the key here is to keep them as small and focused as possible. These groupings are going to breakdown into MRs eventually, and you certainly want contributions of this type to be small and focused to make reviewing and merging the changes easier and worry-free.

    Seriously, if you’re not physically nodding your head right now then I encourage you to go back and re-read the previous paragraph, it’s that important that its message resonates with you. An example of not following this guideline is one we see all too often with junior engineers: “Oh this codebase is using a mix of tabs and spaces? Lemme just helpfully run a code fixer over the whole thing and submit that as one huge merge request! My boss will be so happy!” Likely not, friend, sorry. A better strategy would to group the messy files and call it the “Style Fixes” group and grade it low severity.

    Your grading scale can be whatever makes sense, I use LOW/MED/HIGH or sometimes A through F.

    Eliminate It

    If you’re at this step in your tech-debt-purging process and you’re feeling confident, you ABSOLUTELY SHOULD BE! It takes a lot of work to get here when using this framework. By this point, you’ve done all the following:

    • Attempted to prevent technical debt from occurring in the first place with automated tools like linters and manual tools like code reviews
    • Discussed and understood where the tech debt comes from so you can be on the lookout for it in the future
    • Set up a regular review of your technical debt so that you can constantly re-evaluate it, identify patterns, and otherwise stay in control of the list instead of letting it control you
    • Divided up your codebase into the areas needing the most attention and made a plan for addressing the key issues in each area

    Now…it’s time…to call the Ghostbusters. Just kidding, it’s all on YOU and your team now. It’s time to roll your sleeves up and dig in, one section at a time, addressing the most critical tech debt first and making sure you make small, test-covered changes (where possible) in an effort to clean up the mess that Rob the Manager made last summer when he wrote that login page himself. This can be a slow dredge at first, BUT once the ball really gets rolling it can be a very exciting time on a project. Just approach this work with the same excitement commitment to diligence as you approach feature development (easier said than done, but I believe in you) and you’ll be making huge strides.

    Review

    Here’s the PURGE framework again, for quick reference:

    • P – Prevent It
    • U – Understand It
    • R – Review It
    • G – Group & Grade It
    • E – Eliminate It

    I’ll be honest, this framework is geared towards larger projects with 5+ engineers more than little projects with one or two. Why? Because, when it’s just two engineers personally I feel way more comfortable ripping stuff out indiscriminately on a Thursday night and putting it back together in a cleaner fashion, finally typing git push at 3AM that Friday morning. But, this is just not tenable for most enterprise-grade software projects. For those, we have not only our fellow engineers, but also stakeholders to whom we are accountable. We must move carefully in order to avoid breaking things (even if our job is to move fast and break things, we shouldn’t BREAK break things).

    If you or your organization are facing down a sizeable backlog of technical debt, I do recommend trying my framework to see what kind of footholds you are able to get on the mountain. I think you (and your team, and your stakeholders, and ultimately your customers) will be very pleased, and if you meet with success I encourage you to comment here and let me know!

  • PHPUnit tips for debugging tests that can time travel

    I had to go nine rounds recently with a really elusive test situation where certain tests would fail downstream, but only if certain OTHER tests were enabled and running in the test suite. This also meant that these same tests, failing in every other run, would pass when run by themselves. They also (for fun I guess) would pass perfectly in the CI/CD pipeline but not on dev, but only occasionally. It felt like the tests were hopping in a TARDIS and time-traveling back and forth to pass-land and fail-land right in my terminal.

    ARRRRRRRRRRRRRGGGGGGGGGGGGGGGHHHHHHHHHHH!!!!!!!!

    Sometimes our test suites can make us feel like that. But, we love them anyways because they take care of us and keep us from shipping bad code, so we continue to tolerate them. In my particular case, though, the work was made less painful by my good fortune to have a very well-informed colleague helping me debug. He taught me three neat tricks that helped speed the process along and get us to a solution:

    Tip 1: Create custom test suites, and quarantine the bad tests in a separate suite

    This one was so simple I couldn’t believe I hadn’t thought of it, but you don’t need to have all your tests in one test suite. Create a separate suite for just the bad ones, and then examine them one by one until you find the issue.

    <!-- the whole enchilada: not helpful -->
    <testsuite name="app">
        <directory>tests/TestCase/</directory>
    </testsuite>
    
    <!-- just the bad tests, and whatever else you want -->
    <testsuite name="debug">
        <file>tests/TestCase/FailingTestClassA.php</file>
        <file>tests/TestCase/FailingTestClassB.php</file>
        <file>tests/TestCase/FailingTestClassC.php</file>
    </testsuite>

    …and then run just that suite:

    $> vendor/bin/phpunit --testsuite debug

    This is also helpful for when you know you’re going to be running the same tests over and over again (like when doing TDD) and you don’t want to run the entire app test suite each go. Careful, though, because you might not be informed when you break something downstream.

    Tip 2: Run your test suite n times to discover larger patterns that were previously hidden

    I didn’t even know PHPUnit could do this, but it makes so much sense:

    $> vendor/bin/phpunit --testsuite asdf --repeat 10

    This will run the asdf test suite 10 times. This came in SUPER handy when I was debugging tests that would pass sometimes, fail sometimes (database race conditions, yay….). By just running the test in a single pass one at a time, I was missing larger patterns that became apparent when running it 10 times, then 100, then 1000…

    Sidebar: I am blessed that my app’s test suite currently runs in 4 seconds, so that last run may have taken a little over an hour, but at least it wasn’t four weeks like some projects.

    Tip 3: Experiment with some of the other formats

    Most people just use the default PHPUnit output format and never look at any of the other pretty ones. Check out the clean looks of --testdox!!!:

    # (if your terminal supports ANSI, try it with --colors)
    Application (App\Test\TestCase\Application)
     ✔ Bootstrap loads correct plugins
     ✔ Cli bootstrap loads correct plugins
     ✔ Bootstrap loads correct plugins in debug mode
     ✔ Cli bootstrap loads correct plugins in debug mode
     ✔ Bootstrap does not halt when exceptions occur
     ✔ Bootstrap loads correct middlewares
     ✔ Bootstrap loads correct config keys
    
    # skipped tests look like this
    Skipped Class (App\Test\TestCase\Foo\SkippedController)
     ∅ My skipped test

    How extremely, extremely readable. Also, if you practice good test naming schema, the output is very human-friendly. This output also quickly highlights which areas of your test suite have tests that you haven’t implemented yet or just marked incomplete/skipped because you’re lazy because testing is very hard and I don’t wanna do it anymore because the release is due 🙂

    Bonus tip for PhpStorm users: Run History

    I was also taught this which I found delightful: in the Run tool in PhpStorm there is a clock icon on the left-side-middle of the pane called Test History (screenshot below):

    If you click it, you’ll get a dropdown of your last 10-15 test runs and you can pull up those results for quick inspection. It’s REALLY handy for seeing your test run results over time, and the menu is also filterable, so you can type “my-run-config-name” to filter the entries down to just your preferred run configuration.

    Conclusion

    That’s it for the tips, thanks for reading. I found these to be very helpful for me in my recent work with PHPUnit 9 in uncovering a really nasty series of bugs…hope you find them useful in your work as well. Contribute to Open Source!

  • Exhaustive validation tests: is the juice worth the squeeze?

    (please forgive the title, a textbook example of Betteridge’s Law of Headlines)

    I was working on a new project recently for which one of the requirements was very high test coverage. An interesting twist though was that the data models needing validation were external to the my project’s source code. I had no control over the schema, field alignment, or any naming conventions. I had to make do with what I was given.

    The objects which required validation had classes which provided hooks for attaching one or more validation rules to each field. While custom validation rules were supported, the vast majority of rules were the validation rules built-in to the framework (CakePHP v4), such as minLength or isInteger. So I set out to writing validation tests for each field that exercised all of the validation rules attached to the model…

    …and two hours later I only had three tables done. What? That’s not very efficient, how did that happen? These tables weren’t that large, maybe 10-15 fields apiece. That’s when I remembered an adage taught to me by a (very grumpy, very high-level) mentor many years back: when you’re writing tests that take a long time to write, ask yourself: “Is the juice worth the squeeze?”

    What do you think oranges think when we squeeze them?
    Photo by Ju00c9SHOOTS on Pexels.com

    Granted, this is a bit of a hard metric to gauge by. At least it always has been for me. But in this particular situation with my tests, I think it was pretty clear I was exhausting a lot of effort for not-so-much gain. Why? Because although I thought I was testing MY tables, in fact what I was actually testing was the FRAMEWORK’S validation code. Look at this code sample (it’s for a CakePHP 4 project), ask yourself what is it really testing?

    $badEmails = [
        'abc-@mail.com',
        'abc..def@mail.com',
        '.abc@mail.com',
        'abc#def@mail.com',
        'abc.def@mail.c',
        'abc.def@mail#archive.com',
        'abc.def@mail',
        'abc.def@mail..com'
    ];
    
    foreach ($badEmails as $email) {
        $user = UserFactory::make(['email' => $email])->getEntity();
        $validate = $this->validator->validate($user->toArray());
        // $validate is a nested array of validation errors
        $this->assertNotNull(Hash::get($validate), 'email.email');
    }

    This code will apply each of these “bad” emails to the user entity and run the validation checks on it, and the built-in email validation rule will run for each email and either register an error or not (if valid). So what is the System Under Test (SUT) here? It’s it my entity, or is it the built-in validation ruleset in the underlying framework? (hint: it’s the latter)

    System under test (SUT) refers to a system that is being tested for correct operation. According to ISTQB it is the test object. From a Unit Testing perspective, the SUT represents all of the classes in a test that are not predefined pieces of code like stubs or even mocks.

    From Wikipedia, the free encyclopedia

    Where’s the benefit here? Where’s the juice for my squeeze? If I am not actually testing the SUT, then there is no juice. The juice in fact lies in the framework tests, because you know they’re already testing the crap out of these validators every release. If they weren’t, would they be a competitor in the open-source PHP web framework space? Not a chance. So why am I duplicating their efforts? Is there a way I can get juice of a different form from my model layer tests so that I don’t just have ZERO tests on them?

    Turns out there is. Check out this code (again, this is CakePHP 4 code):

    // helper_functions.php
    private function getValidationRulesForField(Validator $validator, string $fieldName): array
    {
        $fieldRules = $validator->field($fieldName)->rules();
    
        return collection($fieldRules)->map(function ($rule) {
            return $rule->get('rule');
        })->toArray();
    }
    
    // my_test.php
    $nameRules = $this->getValidationRulesForField($this->validator, 'name');
    
    $expected = [
        'scalar' => 'isScalar',
        'maxLength' => 'maxLength'
    ];
    
    $this->assertEquals($expected, $nameRules);

    This code will use a helper method (which I have conveniently stashed in a Trait in my code, but you may handle it however you wish) to assemble a list of validation rules that will be applied to the entity when it validates. Then, I just assert off that list. This allows me to confidently state the following:

    1. All of the validation rules we expect will be applied to the entity on save
    2. The validation rules will be applied in the order we expect
    3. If additional validation rules are added, our tests will fail and we know we need to update them
    4. If validation rules are removed, our tests will fail and we know we need to update them

    That’s a GOOD amount of juice from those four assertions! If you use the above code snippet for all of the fields in each of your entities, that’s a really good baseline level of code coverage for a field that has no other tests whatsoever.

    Important note: when I say “baseline”, I do mean baseline. It is more likely than not that some of your fields could use more validation than the simple test above. If that is the case, you should most definitely expend the effort to add the extra tests. Why? Because, in that case the juice would be worth the squeeze.

    With the above method, I got to a total of 72 tests across three interconnected entities in my application’s data layer (granted, this does include the couple of tests I wrote before I wrote the above code). I was able to write the bulk of these tests in about an hour of extra work, as it was largely copy / paste / update field names.

    I urge you to consider whether exhaustive validation tests are bringing you enough value to justify the time spent on them, or whether a few quick-and-dirty tests (in combination with upstream third-party tests you’re confident in) is sufficient to produce a high-quality, well-tested modern web application. Good luck!

  • Portfolios and Code Rot

    I had someone recently ask me for some samples of my work. “Sure,” I said, “Just check out my GitLab…”

    Uh-oh, it turns out they’re not technical and can’t read code. They need pictures and screenshots. “Do I even have those?”, I ask myself. It turns out, the answer was mostly “no”.

    It seems that in all the years I’ve been doing professional software development, I was unconsciously making the assumption that people who wanted to verify my skills could do so simply by looking at my code. Dumb mistake, and one I regret. Of course there are folks who don’t read code who want to know what I can do. How do I ensure they are taken care of? Do these sites I’ve made over the years even still boot? The answer, as I found out last night, is sadly mostly “no”.

    MIT, Adobe aim to end 'code rot' by letting software auto-optimize -  ExtremeTech
    Image courtesy of extremetech.com

    In my portfolio, there are numerous sites that were made on older versions of frameworks (some even pre-dating Composer, if you can believe it), sites without READMEs, sites with docker-compose files so complex even I can’t parse them anymore…the list goes on and on. In essence, a big ol’ stinking pile of code rot! Unfortunately code rot is a semi-common occurrence in the industry, and just something we have to deal with, however I learned a very important lesson from this experience:

    When you finish building something cool, TAKE a SCREENSHOT. Screenshots are timeless, require no setup, and instantly communicate value and skill to stakeholders, especially non-technical ones.

    Me

    So, while I am trying to get 4 or 5 old sites booted up (yes, I am still going through with it) just so I can take screenshots to share with a third party, I am ruminating quite deeply on this lesson. Take it from me, code rot is unavoidable, but screenshots are forever. Keep your portfolios happy and healthy with plenty of images and context.

    My colleague also had this to say:

    …every once in a while I would do a screen recording with a voiceover walking through some of my portfolio work. 1 to 2 minutes. People seem to enjoy it.

    Rob H.

    I’ve actually also done this before, but for my day-job projects, not for my contracting gigs. It’s worth thinking about, if you have a good screen recording program, a decent mic, and basic video editing skills.

    Do you have any clever solutions for maintaining a portfolio over a long period of time, or for handling long-ingrained code rot? Drop a comment below, I’d love to chat about it.

  • Inverting the Strategy: Think-First Development vs. Code-First Development

    There are a million differentiators between juniors and seniors (sidebar: if you’re a junior looking to level up, check out this awesome list of tips) but in my experience the biggest one is how they approach the creation of new features. In a nutshell, juniors jump right in and start coding. Seniors think about the problem thoroughly and plan their approach before ever writing a line of code.

    Making messes is fun (but risky)

    Back when I was in college and I billed myself as an “indie developer” (that’s code for junior) my coding style was very experimental. I would take a given set of requirements, write a bunch of code really really fast, throw it at the wall and see what stuck, then iterate off of that. Rinse and repeat. And, I have to admit, for smaller projects and scrappy startups, this strategy kinda works. It’s also really fun, and it’s still the style I use for hackathons and some personal projects.

    But it’s becoming rarer and rarer that I work on projects that can support this workflow. There are side effects to experimental, code-first development that may not be immediately apparent:

    1. Rapid coding is a hair’s breadth away from sloppy coding. When you’re coding quickly without much forethought, you forget about important things like security concerns and strict input validation.
    2. When coding this way, it’s easy to forget the fine details of requirements and code yourself into a corner without realizing it.
    3. And, perhaps the most serious problem of all, this often leads to snafus that make their way up to production where your users can encounter them.
    1926: Bad Code - explain xkcd
    Credit: xkcd

    Now, I’m not perfect, and old habits can die hard. So sometimes I still catch myself developing this way. However, nowadays I’m using a new strategy which I call think-first development.

    Planning is less fun (but less risky)

    Here are the principles of think-first development:

    1. It is essential to understand the entire scope of the problem prior to beginning development. That means all inputs and outputs, edge cases, use cases, and dependencies.
    2. Talk about, think about, and ask questions about the entire system until you understand it as well as you understand the programming language(s) you’re implementing it in.
    3. Documentation of the above two steps is beyond important, it’s essential. If you skip this step and keep all the design information in your head, you’ll forget and fall back to quick-and-dirty.

    Understanding the full problem scope

    Ah, the age old question. What problem is this system going to solve, and how will it do that? This can range in difficulty from very easy (like coding a login page) to incredibly difficult, sometimes impossible for a single engineer (like the Bloomberg Terminal). I’m going to skip the part where I explain how I’ve come to understand how to understand problem scope, because many have blogged on it and it basically boils down to this:

    Ask “why?” and if you’re still confused, ask “why?” again. Ask “why?” until you can’t anymore.

    Every three-year old ever, and also smart engineers.

    The key takeaway here in the context of this post is this: do NOT write a line of code before you fully understand the problem scope. If you don’t, you’re walking blind into a potential minefield of issues. (experimental code is fine, but no pushes!)

    Talking about the system

    Everybody has a different perspective, and the job of a senior designing a new system is to take all of these perspectives in, discard the ones that don’t make sense, take pieces of the ones that do, and combine them into a cohesive whole.

    I want to be sure I drive this point home since it’s so important: everybody has a perspective, from the juniorest junior to the brand-new BA to the seniorest senior. Now, as may be obvious, there is a lot of lift and noise in involving everyone in the discussion, and often new features don’t have enough lead time to accommodate this level of investigation, so take it from me: the best method for gathering a collective set of opinions is an open forum. Get everybody into a room/Zoom and start asking questions and generating discussion. Don’t forget to set up follow-up sessions if needed!

    Don’t write code until every possible avenue has been explored and either considered or discarded. Everybody has an opinion, but a smart engineer knows how to sift through these and come up with a cohesive design plan!

    Document everything, and document some more

    I really wish I could share with you my latest system design document, because it’s really a work of art! Many hours (OK, forty) were put into creating it but it’s client-specific and I’m not in the business of leaking IP. Here’s what I’m going to do instead: below you’ll find a list of the key sections that I write when I create any standard SDD (System Design Document). Each has many, many subsections specific to the project or feature I’m designing. Also, images, links, blurbs, pseudocode, anything to demonstrate a key understanding of what will be developed.

    • Guidelines – What are the core tenets of the design of the system? Things that should never be violated.
    • User Stories – Which stories (link to tickets) are going to makeup the work?
    • High-Level Plan – What are steps 1, 2, 3, 4…of your design? Rough outline only.
    • Ticket Flow – Which ticket will be worked on first, second, third…?
    • UI/UX Summaries – Include mockups and screenshots.
    • API Endpoints & Integrations – Which 1st- and 3rd-party APIs will be called?
    • Required Architecture – Which components will be required in the system?
    • Open Questions – What are the outstanding questions about the system?

    Sidenote: I have an article in my backlog to write about the Power of Not Knowing. Stay tuned!

    Jamison

    This document is so, so critical to the successful development of the feature/module/system. It should be the first document you create and the last one you save before actually starting implementation. I’m not messing around on this one! Skip this step and you’ll regret it 😉

    Skipping Steps Improved HD Template | Skipping Steps | Know Your Meme
    Don’t be like this guy. Great way to stretch an ACL! Credit: knowyourmeme

    Conclusion

    The key point that I’m trying to get across here is that juniors jump right into coding and make mistakes. Seniors take time to think about the code they’re going to write before writing it. The difference is that the seniors will have a fully-mapped-out mental model for the system prior to building it, and the build takes infinitely less time than the scrappy, build-as-you-go model that is favored among those with less XP.

    That’s all for now. If you have links to cool SDD’s that you can share, by all means drop it in the comments!

  • Slices and Layers: The Proof

    woman writing on notebook

    I have a success story to share, and it’s proof that as engineers we should Never Stop Learning (sidenote: this is also my personal credo). Recently, a colleague introduced me to this Agile paradigm:

    https://www.thoughtworks.com/insights/blog/slicing-your-development-work-multi-layer-cake

    The paradigm explains that development work can be though of as a cake with many layers. Each layer represents fully-fleshed out functionality in a workflow. For a user workflow, this might mean everything from account creation, to email confirmation, to password reset, to email list signup. Lots of functionality there.

    But what if your client wants to see and end-to-end view of MULTIPLE workflows without going through all the steps of every single one? Using the same analogy, Mr./Mrs. Client may want a demo of the social networking features you’ve added recently, which not only involves account creation, but adding friends and leaving comments on their pages. That involves implementing multiple layers, right? Would take weeks of dev work?

    Fortunately, not necessarily so. What if you did your development work as a slice of the cake, instead of a layer at a time? If you mocked up the processes that weren’t needed for the demo and had them pass the user through the flow so that they could be dropped into the next one (and so on). At the end, you would be able to demonstrate the full functionality of the site, without the little details being implemented yet. Awesome demo? Check. Clear path forward? Check. Clear idea of what functionality still needs to be implemented? Check, check, check.

    Time for the success story. I tried this process recently, because I was looking at some intense functionality that needed to be implemented. In our case we needed to integrate with an online order fulfillment system and a license management API so that customers who purchased products could purchase and access digital content. I thought we were weeks out from demoable code because I thought that every single detail of every layer needed to be implemented. Oh, was I happy to be wrong! Using the slice paradigm, I was able to implement just what was needed in every layer in order to get the user to the next step in the overarching workflow. And wouldn’t you know it, I delivered the demoable code in a fraction of the time.

    It’s so, so important for developers of all levels (seniors and principals included) to realize that there is never a moment in their career where there is nothing to be learned. I’ve been coding for nearly 15 years now (not as long as many in the industry, I realize, but still a respectable amount of time) and I learn something new virtually every day. And then, I learn something huge like this and it rewrites my whole perspective on software development. I feel very fortunate to work with the people that I do, because they are extremely intelligent and always teaching me new tricks.

    Go learn something. Today! When you finish, reward yourself with a SLICE of CAKE 🙂

  • Quick Tip: Testing Feature Branches with Composer

    I was adding a new feature to my CakePHP SecureIDs Plugin today and needed to test it against a living, breathing CakePHP app. Rather than spin up a brand-new one, I decided to use an existing app I had on hand for a contract I have open. Thing was, I didn’t want to go through the whole rigamarole of creating a release, pushing it, waiting for it to hit Packagist, waiting for Composer to see it, and only then being able to add it as a dependency to my app.

    Composer is a smart tool, and so is git-flow. Surely I can combine the two to bypass the above lengthy process and get testing faster, I thought. Turns out it’s totally possible, here’s how I did it in a few easy steps (I’ll go into greater detail in a minute):

    1. Start a feature branch in the plugin repo
    2. Push the feature branch to the remote
    3. In the test app’s composer.json add the repository the plugin sits in
    4. Add the dependency with the version string dev-feature/feature-name

    For what it’s worth, I didn’t think this was gonna work because I was sure Composer was gonna choke on the / in the branch name…but nope! Handled it just fine. 👌

    Breakdown

    So what does all this mean? In short, the steps above describe a way to short-circuit the package release process used in testing dependencies with Composer. Normally it’s a lengthy process with lots of steps in between committing your code and actually seeing it work in a living-breathing app, but the process I described above is a better way if you’re just testing things or making temporary changes. Don’t use this process to release code into the wild, it’s not a good idea.

    In order to use the steps above, your repo must be git-flow enabled. Git flow is a very powerful tool that I’ve used for years to keep my development process sane and well-organized, especially when working in teams. I don’t have time to evangelize GF properly here, so I’ll leave that up to some other qualified individuals: