35 posts —

ai

AI is unlikely to replace software engineers outright because engineering is fundamentally about judgement, tradeoffs, and direction, even as AI automates more manual implementation work.

read full post →
ai practices

Building an always-on automation pipeline around AI coding agents shifts your focus from writing code to writing clear specs, because the quality of the agent input directly determines the quality of the output.

read full post →
ai

A practical snapshot of integrating AI coding agents and Cursor into a daily engineering workflow in 2025, moving beyond autocomplete to agents handling research, planning, and full development tasks.

read full post →
ai testing practices

AI assistants are most valuable when used as an interactive pair programmer for dialogue, test generation, and code review rather than just autocomplete suggestions.

read full post →
database

A database index is a key-value reverse-lookup structure that lets the query engine skip full table scans, dramatically speeding up queries at the cost of additional storage and write overhead.

read full post →
practices

Before investing in a performance refactor, isolate the slow code and measure the maximum possible improvement to ensure the effort is worthwhile and you are targeting the right bottleneck.

read full post →
php

PHP offers several approaches to asynchronous processing - from promise-based libraries to child processes and job queues - to offload blocking work like bulk email sending away from the main web request.

read full post →
practices

Feature flags enable safe, incremental rollouts but require disciplined lifecycle management to prevent stale flags from accumulating as technical debt and causing production incidents.

read full post →
career

Technical interviews are imprecise by nature, so focus on creating a comfortable environment, use open-ended questions, and evaluate problem-solving approach and communication rather than just correct answers.

read full post →
practices php

Dependency Injection Containers separate object instantiation from usage by passing dependencies through constructors, producing loosely coupled, easily testable, and more flexible code.

read full post →
career

Set fixed start and end times, take proper breaks, and dedicate a single room as your workspace to maintain productivity and a healthy work-life balance when working from home.

read full post →
practices

Build software in three sequential steps - make it work, make it right, then make it fast - to avoid premature optimisation and ship at the appropriate quality level for each stage.

read full post →
practices

Effective code reviews require authors to separate ego from their code and reviewers to communicate feedback with good intent, so both parties can collaborate toward a better solution.

read full post →
testing

TDD is less about writing tests first and more about incorporating unit, integration, and user acceptance tests throughout development to catch bugs early and build confidence in your code.

read full post →
practices php

The Facade design pattern creates a simplified interface over complex subsystems, hiding implementation details and reducing repetition without over-complicating the codebase.

read full post →
elixir

Elixir umbrella apps add complexity without delivering true service isolation, and are better replaced by well-structured contexts within a single application in most real-world scenarios.

read full post →
practices

Each small hack or shortcut added to a codebase silently lowers the bar for the next one, and without discipline this attitude snowballs into crippling technical debt.

read full post →
elixir testing

A recap of LoneStar ElixirConf 2019 in Austin covering highlights like the Nerves embedded systems keynote, LiveView, testing strategies, and the welcoming Elixir community.

read full post →
elixir

A GenServer is a good fit for handling concurrent, asynchronous jobs like asset migrations where a GraphQL mutation must trigger background work without blocking the HTTP response.

read full post →
elixir

Use a hexagonal architecture to wrap third-party libraries in a single internal module, keeping business logic decoupled from external dependencies and making future library swaps manageable.

read full post →
elixir

Tasks are often a simpler and better fit than GenServers for one-off async work in Elixir, and understanding the difference helps avoid over-engineering with unnecessary process abstractions.

read full post →
database

Offset pagination is simple but becomes slow and inaccurate at scale, while cursor pagination is more performant and stable for large or frequently changing datasets.

read full post →
elixir

Elixir protocols implement polymorphism by defining a single interface that multiple data types can implement independently, decoupling calling code from type-specific implementation details.

read full post →
elixir

exdocker is an Elixir CLI escript tool that automates the generation of Docker configuration files for Elixir projects, eliminating repetitive copy-paste setup when starting new applications.

read full post →
elixir

Elixir Tasks abstract over processes to provide a clean API for fire-and-forget or awaitable async work, and re-implementing the module from scratch is a great way to understand how they work.

read full post →
elixir

Elixir leverages the Erlang VM to spawn thousands of lightweight processes concurrently, isolating failures and preventing a single bug from blocking or crashing the entire application.

read full post →
elixir database

Ecto lets you build Elixir database queries as composable, chainable expressions using the pipe operator, keeping queries modular, reusable, and easy to extend.

read full post →
elixir

Elixir Streams provide lazy, composable evaluation of data transformations over collections, avoiding loading entire datasets into memory by deferring computation until enumeration.

read full post →
elixir

Implementing a FIFO queue in Elixir using singly-linked lists requires enqueue, dequeue, peek, and count operations, and leverages the prepend-then-reverse pattern for efficient list access.

read full post →
elixir

Elixir Plugs are simple function or module transformations on a Conn struct that can be chained together in a pipeline, making web request handling modular and easy to reason about.

read full post →
elixir

Elixir supervision trees isolate process failures so individual processes can crash and restart independently, similar to how React component error boundaries catch errors in a component tree.

read full post →
practices

Technical debt is unavoidable but manageable - always write tests even for imperfect code, take shortcuts only under real pressure, and continuously refactor before the debt becomes unworkable.

read full post →
elixir

Pattern matching in Elixir replaces complex if-statement chains with declarative destructuring and multi-clause functions, making code control flow clearer and more expressive.

read full post →
elixir

A JavaScript developer attracted to functional programming shares their first impressions of Elixir, finding its pattern matching, immutability, and pipe operator a natural and enjoyable step up from FP in JS.

read full post →
testing

Unit tests verify that isolated functions return expected outputs for given inputs, and teams should start small with imperfect coverage rather than avoiding tests altogether due to disagreement or time pressure.

read full post →