<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Engineer — Software — Portfolio and notes</title>
  <subtitle>Engineer ApS — software development &amp; IT consulting in Copenhagen, Denmark. A portfolio of proven engineering achievements across software, data, cloud and IT.</subtitle>
  <id>https://software.engineer.company/</id>
  <updated>2026-09-13T01:45:50+02:00</updated>
  <author>
    <name>Engineer ApS</name>
    <uri>https://software.engineer.company/</uri>
    <email>welcome@engineer.company</email>
  </author>
  <rights>Copyright © 2025 – present · Engineer ApS</rights>
  <generator uri="https://gohugo.io/">Hugo 0.166.0</generator>
  <icon>https://software.engineer.company/assets/icons/apple/apple-touch-icon.png</icon>
  <logo>https://software.engineer.company/assets/images/brand/card.webp</logo>
  <link href="https://software.engineer.company/" rel="alternate" type="text/html" />
  <link href="https://software.engineer.company/atom.xml" rel="self" type="application/atom+xml" />
  <entry>
    <title>Designed a comprehensive infrastructure framework for DTU impacting 14 departments, featuring flexible modules, unified data pipelines, and structured support strategies for long‑term adoption.</title>
    <id>https://software.engineer.company/portfolio/designed-a-comprehensive-infrastructure-framework-for-dtu-impacting-3/</id>
    <link href="https://software.engineer.company/portfolio/designed-a-comprehensive-infrastructure-framework-for-dtu-impacting-3/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Stakeholder &amp; Reporting" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Technical Documentation" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>Designed a comprehensive infrastructure framework for DTU spanning 14 departments — modular, with unified data pipelines and support strategies.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; At a research institute comprising 14 diverse research groups, each group worked with varying data sources, formats, scales, and software tools. The technical expertise and available IT resources varied widely across the groups. While a few had managed to create and deploy custom IT solutions, many struggled with the complexity of their data infrastructure needs, diverting valuable time and focus from their core research work.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The task was to devise a solution that would allow researchers to focus on their scientific work rather than IT challenges. The goal was to design and implement a scalable, institute‑wide data infrastructure that could accommodate the broad and differing requirements of the majority of research groups.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; A robust, forward‑thinking infrastructure plan was developed that balanced flexibility and standardization. The plan outlined key components such as modular architecture, integration pathways for diverse data sources, user‑friendly interfaces tailored to varying technical skill levels, and scalable storage and processing solutions. It also included strategies for onboarding, support, and governance to ensure adoption and sustainability.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The resulting infrastructure plan was both technically sound and strategically aligned with the institute’s research goals. It unified the vision for data management across the organization, provided a clear path to reducing IT burden on researchers, and laid the foundation for a shared, efficient, and future‑ready research data environment. The plan was well received for its inclusivity, clarity, and adaptability, setting a strong direction for the institute&amp;rsquo;s data infrastructure transformation.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Accelerated geographical data pipeline performance by 50x by improving SQL programming and data modeling across PostgreSQL, MS SQL, and Google Cloud BigQuery.</title>
    <id>https://software.engineer.company/portfolio/accelerated-geographical-data-pipeline-performance-by-50x-by-5/</id>
    <link href="https://software.engineer.company/portfolio/accelerated-geographical-data-pipeline-performance-by-50x-by-5/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Data Warehousing" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="Data Warehouse Design" scheme="https://software.engineer.company/services/" />
    <category term="Database Performance Tuning" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <summary>Accelerated a geographical data pipeline 50x through SQL and data-model tuning across PostgreSQL, MS SQL and Google BigQuery.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The organization faced significant delays in processing large sets of geographical data, which affected the efficiency of data‑driven decision‑making processes and analytical reporting.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The objective was to optimize the geographical data pipeline to enhance performance and reduce processing time, ensuring that data analytics could be performed more effectively and in real‑time.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The existing SQL programming and data modeling structures across PostgreSQL, MS SQL, and Google Cloud BigQuery were meticulously analyzed, which surfaced bottlenecks related to inefficient indexing strategies, suboptimal query designs, and lack of appropriate constraints. To address these issues:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The data models were redesigned to normalize critical datasets, with partitioning strategies implemented to improve data retrieval times.&lt;/li&gt;&#xA;&lt;li&gt;Applied advanced indexing techniques, including B‑tree and GiST indexes for PostgreSQL, filtered indexes in MS SQL, and clustering in BigQuery.&lt;/li&gt;&#xA;&lt;li&gt;Optimized complex SQL queries by refactoring subqueries, reducing join operations, and incorporating materialized views where relevant.&lt;/li&gt;&#xA;&lt;li&gt;Established data integrity constraints such as foreign keys and check constraints to ensure data consistency without compromising performance.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; These comprehensive optimizations accelerated the geographical data pipeline performance by 50x, significantly reducing data processing time. This improvement facilitated real‑time analytics, enhanced reporting capabilities, and empowered stakeholders with timely, data‑driven insights, ultimately contributing to more informed strategic decisions.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Improved geographical map application performance by 10x through strategic database transition from MSSQL to PostgreSQL, optimizing processing and data security.</title>
    <id>https://software.engineer.company/portfolio/improved-geographical-map-application-performance-by-10x-through-6/</id>
    <link href="https://software.engineer.company/portfolio/improved-geographical-map-application-performance-by-10x-through-6/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Migrations &amp; Modernization" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Database Migration &amp; Modernization" scheme="https://software.engineer.company/services/" />
    <category term="Database Performance Tuning" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <summary>Improved a GIS map application 10x by migrating MSSQL to PostgreSQL — spatial queries dropped from 2.5s to under 250ms.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The organization’s geographical map application, which supported real‑time spatial queries for many users, was experiencing severe performance bottlenecks. Latency in rendering map layers and querying location‑based data impacted both user experience and backend service reliability. The system was backed by a legacy Microsoft SQL Server (MSSQL) database that lacked native geospatial indexing.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Improve the performance, scalability, and security of the map application, with a specific goal of reducing query latency and increasing throughput for spatial operations.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Led a strategic database migration from MSSQL to PostgreSQL with the PostGIS extension to enable native geospatial support.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Designed a new schema optimized for spatial data, introducing GIST and SP‑GiST indexes on geometry and geography columns for faster querying.&lt;/li&gt;&#xA;&lt;li&gt;Defined strict foreign key and check constraints to ensure relational integrity and enforce data validation rules on spatial coordinates.&lt;/li&gt;&#xA;&lt;li&gt;Migrated over 50 million spatial records using ETL pipelines with data transformation steps to conform to new SRID standards (EPSG:4326).&lt;/li&gt;&#xA;&lt;li&gt;Tuned PostgreSQL configuration parameters (e.g., work_mem, effective_cache_size) for optimal I/O performance under concurrent access.&lt;/li&gt;&#xA;&lt;li&gt;Implemented role‑based access controls (RBAC) and row‑level security to enforce data protection policies across multiple user groups.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Achieved a 10x improvement in spatial query performance, reducing average response time from 2.5 seconds to under 250 milliseconds. Backend CPU load dropped by 65%, and system availability improved during peak usage. Security posture was also strengthened with granular access policies and data validation constraints, reducing potential for spatial data corruption.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Resolved 1,000 issues in geographical data and time‑series data, using GDAL, ArcGIS, PostGIS, Mapbox, QGIS, SQL (PL/pgSQL, Transact‑SQL), Bash, ensuring high‑quality big data processing.</title>
    <id>https://software.engineer.company/portfolio/resolved-1-000-issues-in-geographical-data-and-7/</id>
    <link href="https://software.engineer.company/portfolio/resolved-1-000-issues-in-geographical-data-and-7/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <summary>Resolved 1,000+ issues in geographic and time-series big data with GDAL, PostGIS, ArcGIS, Mapbox, QGIS and SQL — 35% faster queries.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; While working on a large‑scale geospatial analytics project, the team encountered numerous inconsistencies and anomalies within the geographical and time‑series datasets. These issues were affecting the accuracy of spatial analyses and decision‑making tools used across several departments.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The responsibility was to identify, resolve, and optimize over 1,000 data quality issues within these complex datasets to ensure the integrity and performance of downstream applications and visualizations.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Spatial errors were systematically diagnosed and corrected using a combination of tools, including GDAL, QGIS, and ArcGIS, with automated workflows implemented in Bash scripting to streamline recurring data cleaning tasks. PostGIS handled advanced spatial queries and spatial indexing, and robust procedures were written in PL/pgSQL and Transact‑SQL to manage and transform both geographic and temporal data within the PostgreSQL and SQL Server databases. Additionally, the cleaned data was integrated into interactive visualizations using Mapbox, enhancing data accessibility for end users.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Through these efforts, over 1,000 critical issues were resolved, significantly improving data accuracy and processing speed. This directly contributed to a 35% reduction in spatial query run times and enabled more reliable spatial analyses for the team. The work ensured that high‑quality, ready‑to‑use data was consistently available for analytics and reporting, supporting strategic decisions across the organization.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Designed, implemented, and administered 6 ETL/ELT pipelines, utilizing Google BigQuery, MSSQL, PostgreSQL, Shell scripting, PL/pgSQL, and Transact‑SQL, integrating data for efficient Python API processing.</title>
    <id>https://software.engineer.company/portfolio/designed-implemented-and-administered-6-etl-elt-pipelines-8/</id>
    <link href="https://software.engineer.company/portfolio/designed-implemented-and-administered-6-etl-elt-pipelines-8/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Cloud" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Data Warehousing" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Networking &amp; VPN" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="Data Warehouse Design" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <summary>Designed and ran 6 ETL/ELT pipelines (BigQuery, MSSQL, PostgreSQL), cutting a 3-hour manual job to under 20 minutes with daily syncs.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The organization needed to consolidate and process large volumes of structured data from a remote data warehouse located behind an IPSec VPN. This data was crucial for powering internal analytics dashboards and external Python APIs used by clients and partners.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The objective was to design, implement, and maintain a set of robust and automated ETL/ELT pipelines to securely retrieve, transform, and load data into Google BigQuery, ensuring data accuracy, performance, and scalability across systems.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; 6 end‑to‑end ETL/ELT pipelines were designed, implemented, and administered, spanning multiple technologies including Google BigQuery, MSSQL, PostgreSQL, Shell scripting, PL/pgSQL, and Transact‑SQL.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Established secure connections to a remote server protected by an IPSec VPN to automate data retrieval.&lt;/li&gt;&#xA;&lt;li&gt;Scheduled and executed downloads of compressed data archives containing Parquet, CSV, and .bak files.&lt;/li&gt;&#xA;&lt;li&gt;Developed Bash and Python scripts to extract and classify files based on type and schema.&lt;/li&gt;&#xA;&lt;li&gt;For .bak files, backups were restored into a local MSSQL Server instance using RESTORE DATABASE workflows and schema integrity validated.&lt;/li&gt;&#xA;&lt;li&gt;Loaded structured data into staging schemas in MSSQL and PostgreSQL, using bcp, psql, and SSIS tools, depending on the source format.&lt;/li&gt;&#xA;&lt;li&gt;Wrote modular and reusable PL/pgSQL and T‑SQL procedures to clean, normalize, and enrich the data based on business logic.&lt;/li&gt;&#xA;&lt;li&gt;Exported curated datasets and tables into intermediate formats, compressed them using gzip, and securely transferred them to a Google Cloud Storage bucket.&lt;/li&gt;&#xA;&lt;li&gt;Automated the upload and schema mapping processes to Google BigQuery, using bq CLI and Python‑based data ingestion scripts.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; These automated pipelines significantly reduced the manual overhead and processing time — from 3+ hours of manual work to under 20 minutes end‑to‑end, while improving data freshness from weekly to daily syncs. The Python APIs consuming this data saw a 30% performance improvement, and the enhanced visibility helped business analysts deliver faster insights to stakeholders. The solution remains scalable and extensible for onboarding new data sources as the business grows.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Delivered 8 Power BI projects with comprehensive manuals, integrating Microsoft Power BI tools with NodeJS API and Python FastAPI for effective data analytics and visualization.</title>
    <id>https://software.engineer.company/portfolio/delivered-8-power-bi-projects-with-comprehensive-manuals-10/</id>
    <link href="https://software.engineer.company/portfolio/delivered-8-power-bi-projects-with-comprehensive-manuals-10/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Analytics" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Analytics &amp; BI Dashboards" scheme="https://software.engineer.company/services/" />
    <category term="Technical Documentation" scheme="https://software.engineer.company/services/" />
    <summary>Delivered 8 Power BI analytics projects (Node.js API, Python FastAPI) with full manuals, improving report generation efficiency by 60%.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The organization needed dynamic, visual insights into complex datasets involving electrical grid performance and geographical distribution metrics to support decision‑making across technical and strategic teams.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Develop interactive dashboards and reporting solutions that could effectively present both real‑time and historical geographical and electrical data, enabling stakeholders to quickly identify trends, anomalies, and performance indicators.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Integrated Microsoft Power BI with a custom backend stack using NodeJS API and Python FastAPI to streamline data ingestion, transformation, and visualization. Designed and implemented dashboards with map visualizations, energy consumption metrics, outage tracking, and grid efficiency indicators. Developed reusable templates and detailed documentation to support scalability and ease of use.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Successfully delivered 8 data analytics projects, improving report generation efficiency by 60% and enabling cross‑functional teams to make faster, data‑driven decisions. Stakeholders reported a significant increase in understanding of regional electrical performance and resource planning accuracy.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Released 500 electricity and GIS data analysis reports, utilizing deep research and troubleshooting to ensure accurate geographic and time series big data insights.</title>
    <id>https://software.engineer.company/portfolio/released-500-electricity-and-gis-data-analysis-reports-11/</id>
    <link href="https://software.engineer.company/portfolio/released-500-electricity-and-gis-data-analysis-reports-11/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Analytics" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Data Warehousing" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Stakeholder &amp; Reporting" scheme="https://software.engineer.company/categories/" />
    <category term="Data Analytics &amp; BI Dashboards" scheme="https://software.engineer.company/services/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <summary>Published 500 electricity and GIS data-analysis reports — a cross-department reference for load balancing, efficiency and anomaly detection.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The team managed vast datasets generated by smart‑meters installed across multiple geographic regions. These smart‑meters produced granular, time‑series electrical consumption data used by energy analysts, engineers, and regional planners for operational and strategic decision‑making.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The responsibility was to produce high‑quality, transparent, and reproducible analytical reports that could uncover patterns in energy consumption, detect anomalies, and identify regional usage trends, while ensuring non‑technical stakeholders could easily interpret and reuse the findings.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; 500+ in‑depth data analysis reports were created and delivered, using pure SQL to perform all data extraction, transformation, and analysis tasks, working directly within cloud‑based environments such as PostgreSQL and BigQuery. The data included geolocation coordinates, meter IDs, timestamped energy usage, and environmental metadata. The SQL scripts featured CTEs, window functions, subqueries, and geospatial joins, allowing for scalable and efficient processing.&lt;/p&gt;&#xA;&lt;p&gt;Each report included annotated SQL code, enabling colleagues and collaborators to fully reproduce and audit the research, which significantly reduced the time needed for follow‑up analysis. Troubleshooting notes were also added and common data quality issues documented, such as missing GPS coordinates or corrupted meter values, with recommended handling procedures.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The reports became a standard reference across departments, aiding in regional load balancing, energy efficiency planning, and anomaly detection. By ensuring full transparency and reproducibility, the work helped improve stakeholder trust in the data and contributed to more accurate forecasting models and a 10–15% improvement in operational planning efficiency.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Architected, created, and managed 100 PostgreSQL, MS SQL, and Google BigQuery data warehouse databases with primarily GIS and time‑series data, optimizing performance and scalability.</title>
    <id>https://software.engineer.company/portfolio/architected-created-and-managed-100-postgresql-ms-sql-12/</id>
    <link href="https://software.engineer.company/portfolio/architected-created-and-managed-100-postgresql-ms-sql-12/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Data Warehousing" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Data Warehouse Design" scheme="https://software.engineer.company/services/" />
    <category term="Database Administration (DBA)" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Database Performance Tuning" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <summary>Architected and ran 100 PostgreSQL, MS SQL and BigQuery data-warehouse databases for GIS and time-series data — query times down 40–60%.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; In a rapidly growing tech company, there was a critical need to create, manage, and optimize a diverse portfolio of 100+ databases across PostgreSQL, Microsoft SQL Server, and Google BigQuery. These databases primarily handled complex datasets, including geographic information systems (GIS) data (e.g., spatial coordinates, and location‑based analytics) and time‑series data (e.g., sensor readings, logs, and real‑time metrics). The existing infrastructure faced challenges with scalability, query performance, and data consistency, particularly as the volume of data increased exponentially. The organization required a robust architecture to ensure data reliability, and reduce operational costs.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The primary objective was to design, implement, and manage a scalable, high‑performance data warehouse ecosystem tailored for GIS and time‑series data. This involved:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Addressing performance bottlenecks in complex spatial and time‑based queries.&lt;/li&gt;&#xA;&lt;li&gt;Ensuring scalability to handle growing data volumes while maintaining cost efficiency.&lt;/li&gt;&#xA;&lt;li&gt;Collaborating with cross‑functional teams (e.g., data scientists, product managers) to align database design with business needs.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; To achieve these goals:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Designed Scalable Architectures:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Created normalized and denormalized schemas for PostgreSQL and SQL Server, leveraging spatial indexing (e.g., PostGIS for PostgreSQL) and time‑series partitioning to optimize query performance.&lt;/li&gt;&#xA;&lt;li&gt;Utilized BigQuery’s time‑partitioned and clustered tables for efficient handling of large‑scale time‑series data.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;Implemented Optimization Strategies:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Introduced query optimization techniques, such as indexing, materialized views, and caching, to reduce latency for GIS and time‑series queries.&lt;/li&gt;&#xA;&lt;li&gt;Applied data compression and columnar storage in BigQuery to minimize storage costs and improve scan speeds.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;Collaborated on Cross‑Platform Integration:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Documented best practices for GIS and time‑series data modeling to guide teams in future projects.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The initiatives led to significant improvements:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Performance Gains: Query response times for GIS and time‑series data decreased by 40–60%, enabling faster analytics and decision‑making.&lt;/li&gt;&#xA;&lt;li&gt;Operational Reliability: Automated monitoring reduced downtime by 50%, while standardized processes improved team productivity and reduced errors.&lt;/li&gt;&#xA;&lt;li&gt;Business Impact: The optimized infrastructure enabled the company to launch new data‑driven products (e.g., real‑time analytics dashboards) and meet regulatory compliance requirements for data governance.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This work solidified the organization’s ability to handle complex data challenges.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Automated GIS SaaS application deployment, data processing, and reporting system using GitHub Actions CI/CD, Python, Bash, and SQL.</title>
    <id>https://software.engineer.company/portfolio/automated-gis-saas-application-deployment-data-processing-and-16/</id>
    <link href="https://software.engineer.company/portfolio/automated-gis-saas-application-deployment-data-processing-and-16/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Cloud" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <summary>Automated GIS SaaS deployment, data processing and reporting with GitHub Actions CI/CD, Python, Bash and SQL — shorter, reliable releases.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; At Utiligize, getting the GIS SaaS app deployed, processing its data and producing the reports were all manual steps — and manual steps are both slow and quietly dangerous. Every release ate engineering time and carried the chance of a slip, and the recurring data and reporting work sat there eating capacity week after week.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Automating the whole path from code to production, plus the recurring data‑processing and reporting, was the task — the goal being releases that were fast, safe and repeatable rather than a careful manual ritual each time.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The whole path got automated. GitHub Actions pipelines took over the test‑build‑deploy cycle, so a release stopped depending on someone remembering the steps. The recurring data processing and the reports moved into scheduled Python, Bash and SQL jobs, so they just ran instead of being someone&amp;rsquo;s chore. And the configuration and secrets were standardised so every environment behaved identically — which is what kills the &amp;ldquo;works on my machine&amp;rdquo; surprises, because there stops being a &amp;ldquo;my machine&amp;rdquo; that&amp;rsquo;s different from production.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Deployment, data processing and reporting all became automated and reliable, the manual toil came off the team&amp;rsquo;s plate, and the release cycle got shorter. The team could put its attention on the product instead of the operations wrapped around it.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Automated delivery of 20 GIS data pipelines and app data ETL processes, streamlining infrastructure automation and reporting.</title>
    <id>https://software.engineer.company/portfolio/automated-delivery-of-20-gis-data-pipelines-and-17/</id>
    <link href="https://software.engineer.company/portfolio/automated-delivery-of-20-gis-data-pipelines-and-17/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Monitoring &amp; Observability" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <summary>Automated 20 GIS data pipelines and app-data ETL processes, streamlining infrastructure automation and delivering dependable, current data.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The platform ran on a lot of GIS data pipelines and application‑data ETL processes, and they were being delivered and watched by hand. Hand‑run pipelines create bottlenecks, they drift out of consistency, and worst of all they carry a constant low risk that one quietly fails and nobody notices until the data&amp;rsquo;s already wrong downstream.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Automating the delivery of those pipelines and ETL processes — so the data flowed reliably and predictably without someone shepherding it — was the task.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Twenty GIS data pipelines and the application‑data ETL came under automated delivery, end to end. The scheduling, the logging and the failure handling got standardised, so every pipeline behaved the same way and, crucially, you could see when one didn&amp;rsquo;t — a silent failure is only silent if nothing&amp;rsquo;s watching. And they were folded into the existing infrastructure automation and reporting, so they were part of one coherent system rather than a drawer full of scripts someone had to remember to run.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; All twenty pipelines and their ETL ran automatically and predictably, and the whole infrastructure‑automation and reporting picture got tidier for it. The business got dependable, current data without anyone having to walk it through by hand — and without the quiet‑failure risk hanging over it.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Led the software development of a GIS map application, driving revenue growth by 10x and positioning the product as a primary data asset.</title>
    <id>https://software.engineer.company/portfolio/led-the-software-development-of-a-gis-map-24/</id>
    <link href="https://software.engineer.company/portfolio/led-the-software-development-of-a-gis-map-24/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Mentoring &amp; Coaching" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Team Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>Led development of a GIS map application that became a platform cornerstone, driving a 10x revenue increase via upsells and data licensing.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The core challenge was to build a platform that could track, monitor, and optimize renewable energy assets like solar panels and wind turbines. However, the initial solution lacked robust geospatial capabilities, making it difficult for clients to visualize asset locations, analyze spatial data, or derive actionable insights. Recognizing this gap, the leadership team prioritized developing a GIS (Geographic Information System) map application to enhance the platform’s value and meet evolving customer needs.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The task was to lead the development of the GIS application, a critical component to differentiate the product in the competitive green energy market. The role extended beyond software development — spanning tech leader, devops engineer, data engineer, and SRE (Site Reliability Engineer), ensuring the solution aligned with the company’s growth trajectory. The goal was to create a scalable, intuitive GIS tool that integrated seamlessly with the SaaS platform, enabling users to visualize asset locations, track performance metrics, and leverage spatial data for decision‑making. This required balancing technical innovation with the constraints of a growing startup, while ensuring the product could evolve alongside the company.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; It began with collaborating with stakeholders to define the GIS application’s core functionalities, focusing on integration with the existing SaaS platform and real‑time data visualization. Given the small team size, a modular architecture was designed using open‑source GIS libraries to keep the system lightweight and scalable. CI/CD pipelines, automated infrastructure provisioning, and monitoring tools were also implemented to ensure reliability. As the team expanded, new engineers were mentored, cross‑functional collaboration facilitated, and user feedback prioritized to refine the application iteratively. Over time, the GIS tool evolved from a basic prototype into a sophisticated platform, incorporating advanced analytics and custom dashboards to meet customer demands.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The GIS application became a cornerstone of the SaaS platform, driving a 10x increase in revenue through upsells, data licensing, and new customer acquisitions. Its ability to visualize green energy assets in real time improved operational efficiency for clients, while the tool’s continuous refinement positioned it as a primary data asset. The success of the project not only solidified the company’s reputation in the renewable energy sector but also demonstrated the value of a multi‑disciplinary approach in a fast‑paced startup environment. By combining technical expertise with strategic vision, the GIS application became a key differentiator, fueling long‑term growth and innovation.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Directed full‑stack GIS map development, overseeing PostgreSQL, Mapbox, ReactJS, and NodeJS to deliver an integrated solution.</title>
    <id>https://software.engineer.company/portfolio/directed-full-stack-gis-map-development-overseeing-postgresql-25/</id>
    <link href="https://software.engineer.company/portfolio/directed-full-stack-gis-map-development-overseeing-postgresql-25/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>Directed full-stack GIS map development (PostgreSQL, Mapbox, React, Node.js), delivering one integrated, extensible core of the platform.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; By this stage the GIS map had stopped being a feature and become the reason customers logged in. The trouble was that it had grown up in pieces. Spatial data lived in PostgreSQL, the map itself was drawn with Mapbox, and the application around it was ReactJS on the front with NodeJS behind. Each part worked on its own. They just hadn&amp;rsquo;t been built to fit together, and the seams were starting to show.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The role covered the full‑stack development of the map and the technical direction that came with it: the data model, the rendering, the API and the React front end. The goal was to turn four things that happened to share a repository into one product worth standing behind.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The architecture was set first, then the work stayed close to the code rather than steering from a distance. On the data side, the PostgreSQL spatial model was kept tidy so queries didn&amp;rsquo;t slow to a crawl as the datasets grew. Mapbox did the drawing; the job was feeding it the right data at the right zoom levels instead of everything at once. On the application side, the ReactJS and NodeJS work got reviewed, the team was pushed toward shared conventions, and responsibilities kept getting moved back to the layer they belonged in whenever one started leaking into the next. A fair amount of it was unglamorous work: catching the small inconsistencies before they hardened into architecture.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; What came out was a single integrated map application, with data, rendering and interface finally pulling the same way. It became a core part of the platform, and something the team could keep extending without it buckling every time a layer got added.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Optimized forecasting and investment strategies for 11 electricity grid operators, driving operational efficiency through data‑driven GIS solutions.</title>
    <id>https://software.engineer.company/portfolio/optimized-forecasting-and-investment-strategies-for-11-electricity-27/</id>
    <link href="https://software.engineer.company/portfolio/optimized-forecasting-and-investment-strategies-for-11-electricity-27/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Analytics" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Stakeholder &amp; Reporting" scheme="https://software.engineer.company/categories/" />
    <category term="Data Analytics &amp; BI Dashboards" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <summary>Optimised forecasting and investment strategy for 11 electricity grid operators with data-driven GIS — trustworthy, targeted planning.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Electricity grid operators live and die on decisions about where to reinforce the network and where to put their money, and eleven of them were making those calls without much geospatial analysis underneath. They had the operational data. What they didn&amp;rsquo;t have was a way to see it on the map, where the patterns actually live.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The job was to sharpen their forecasting and their investment strategies with GIS — to turn tables of readings into something that showed them where capacity was getting tight, where risk was building, and where the next pound was best spent.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Their operational data was brought together with geospatial modelling so the two reinforced each other. Instead of forecasting in the abstract, the grid could be looked at spatially and asked concrete questions: which stretches were heading toward their limits, which areas justified investment first. For eleven operators that meant fitting the analysis to how each of them actually ran their network, not handing everyone the same template and hoping it fit.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The operators came away with forecasts they could trust and investment decisions that were aimed rather than hopeful. Grounding the planning in what the map showed made the whole thing more efficient — money and attention went where the data pointed instead of where habit did.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Presented 200 UI/UX improvements for the GIS map application, boosting revenue by 10x through enhanced software features.</title>
    <id>https://software.engineer.company/portfolio/presented-200-ui-ux-improvements-for-the-gis-28/</id>
    <link href="https://software.engineer.company/portfolio/presented-200-ui-ux-improvements-for-the-gis-28/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Analytics" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Stakeholder &amp; Reporting" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>Delivered 200 UI/UX improvements to a GIS map application, contributing to a 10x rise in revenue — careful UX as a commercial driver.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The map interface had grown powerful and, along the way, complicated. There were places where you could feel customers not getting the value that was sitting right there in front of them — good functionality trapped behind clumsy interactions. That gap between what the product could do and what people found easy to do was quietly costing us.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The job was finding those gaps and pushing the fixes through: the usability work that would make the product easier to get value from and, not by coincidence, more valuable commercially.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Rather than guess at what was wrong, the work went into the feedback and the usage data, and out of that came a backlog of 200 concrete UI/UX improvements. They weren&amp;rsquo;t treated as equal — ranked by impact, with the ones that mattered argued for and taken through the team to ship as real features instead of a wishlist that sat in a document going stale.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The interface got noticeably better to use, and the product&amp;rsquo;s value followed: the work contributed to a tenfold rise in revenue. It&amp;rsquo;s a case worth coming back to, because it makes the point cleanly — careful UX isn&amp;rsquo;t cosmetic, it shows up on the invoice.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Managed the execution of 30 successful GIS projects, demonstrating leadership in delivering innovative solutions across the field.</title>
    <id>https://software.engineer.company/portfolio/managed-the-execution-of-30-successful-gis-projects-32/</id>
    <link href="https://software.engineer.company/portfolio/managed-the-execution-of-30-successful-gis-projects-32/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Agile &amp; Scrum" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Project Management" scheme="https://software.engineer.company/categories/" />
    <category term="Stakeholder &amp; Reporting" scheme="https://software.engineer.company/categories/" />
    <category term="Team Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <category term="Project Management (Agile)" scheme="https://software.engineer.company/services/" />
    <category term="Team Building &amp; Mentoring" scheme="https://software.engineer.company/services/" />
    <summary>Delivered 30 successful GIS projects — a track record of consistent, innovative delivery that earned lasting client trust.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Mappitall made its money delivering GIS projects, and there were a lot of them running at once. The company&amp;rsquo;s growth rode on getting them out the door reliably — not one flagship project done brilliantly, but a steady stream of them landing on time and holding together, which only happens when the coordination across the team is actually working.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The responsibility was getting that portfolio delivered — keeping scope, people and timelines lined up across all of it, and giving the team the direction to ship.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Across those years, the delivery of thirty GIS projects ran through this role. In practice that meant holding the scope steady when it wanted to creep, pointing the right people at the right work, and staying close enough to each project to catch trouble while it was still small. When something was going to slip, better to know early and reshuffle than find out at the deadline. A lot of the job was just keeping the plates spinning and the client&amp;rsquo;s expectations honest about what was coming and when.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; All thirty came in. That consistency mattered more than any single project would have — a client who&amp;rsquo;s seen you deliver thirty times over doesn&amp;rsquo;t worry about the thirty‑first, and that reputation is a good part of why the company kept growing.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Led the development, deployment, and support of over 30 GIS projects, demonstrating expertise in PostgreSQL, Bash, Python, JavaScript, GDAL, ArcGIS, PostGIS, and Mapbox technologies.</title>
    <id>https://software.engineer.company/portfolio/led-the-development-deployment-and-support-of-over-35/</id>
    <link href="https://software.engineer.company/portfolio/led-the-development-deployment-and-support-of-over-35/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Team Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>Led development, deployment and support of 30+ GIS projects with PostgreSQL, Python, GDAL, ArcGIS, PostGIS and Mapbox across the lifecycle.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The company&amp;rsquo;s whole output was made‑to‑measure GIS — custom mapping and spatial systems built to a client&amp;rsquo;s specific problem, then kept running once they were live. Building the thing is only half of it; a geospatial project that ships and then falls over in production hasn&amp;rsquo;t really been delivered.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; These projects ran end to end through this role — the development, the deployment, and the support once they were live — with the technical direction across a fairly wide stack.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Delivery ran on more than thirty GIS projects, hands‑on across the stack the whole way. PostgreSQL with PostGIS underneath for the spatial data, GDAL/OGR for moving it between formats — shapefiles, GeoJSON, GeoTIFF, KML, vector tiles — and QGIS and JOSM for the data work itself. On the front of it, web maps built on Mapbox GL and Leaflet, sometimes against the ArcGIS or HERE APIs, with the app layer in JavaScript, Python, PHP and SQL. The work ranged from 2D and 3D digital mapping through LiDAR processing, georectification and vectorisation to indoor mapping and navigation. And the responsibility ran past the point of shipping — the deployment and the ongoing support in production were part of it too, so problems weren&amp;rsquo;t handed off; the decisions had to be lived with.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Thirty‑plus projects built, deployed and supported across that whole range. Being on the hook for the full lifecycle rather than just the build is what kept the quality honest — you design differently when you know you&amp;rsquo;re the one getting the call if it breaks.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Contributed to user interface design processes, ensuring intuitive, visually appealing, and user‑friendly project interfaces.</title>
    <id>https://software.engineer.company/portfolio/contributed-to-user-interface-design-processes-ensuring-intuitive-36/</id>
    <link href="https://software.engineer.company/portfolio/contributed-to-user-interface-design-processes-ensuring-intuitive-36/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>Contributed to UI design, delivering intuitive, polished and user-friendly interfaces by asking usability questions during design.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The interfaces across our projects were uneven. Some were fine, some had clearly been built by engineers thinking about the data model rather than the person who&amp;rsquo;d have to use it, and the design decisions weren&amp;rsquo;t always made with the end user in the room. On a web‑map product especially, the map is the easy part — it&amp;rsquo;s the controls, the filtering and the flow around it where people get lost.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Getting involved in the UI design process was part of the role — to help make the interfaces something people actually found intuitive and pleasant to use, not just functional.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The role wasn&amp;rsquo;t the designer&amp;rsquo;s, but it sat in the design process and brought an engineering perspective — pushing on layout, on the flow through a task, on whether a screen was actually clear or just familiar to the people who&amp;rsquo;d built it. These were React, Angular and Vue front‑ends sitting over Mapbox and Leaflet maps, and a lot of the usability lived in the details: how you filtered a dataset, how you moved between floors on an indoor map, whether the thing told you what it was doing. Mostly it meant asking the dumb‑user questions early, while they were still cheap to fix, instead of after release when the confusion came back as support tickets.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The interfaces got more intuitive and more polished, which end users felt directly, and it lifted the overall quality of what got put out. Getting the usability questions asked during design rather than after release is most of what made the difference.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Overhauled internal processes, saving 8,000 hours by improving software architecture, systems, and scheduling efficiency.</title>
    <id>https://software.engineer.company/portfolio/overhauled-internal-processes-saving-8-000-hours-by-38/</id>
    <link href="https://software.engineer.company/portfolio/overhauled-internal-processes-saving-8-000-hours-by-38/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Project Management" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Project Management (Agile)" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>Overhauled internal processes to save ~8,000 hours, improving software architecture, systems and scheduling efficiency.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The way things were done internally had accumulated the usual cruft — software architecture that had grown by accretion rather than design, systems that worked but not efficiently, scheduling that left people either waiting or slammed. None of it was on fire, which is exactly why it had been left alone, but it was quietly costing a lot of time.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The aim was to overhaul those processes — to go find the waste and take it out rather than keep paying for it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The internal processes got reworked across three fronts: the software architecture, so it was something you could reason about and build on instead of work around; the systems, streamlined so the routine work stopped taking longer than it should; and the scheduling, so capacity was actually matched to the work. And the changes were made to stick — embedded in how the team operated rather than left as a memo everyone nodded at and forgot — because process improvements that aren&amp;rsquo;t made permanent just decay back to the old way.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The overhaul saved roughly 8,000 hours by making the architecture, systems and scheduling meaningfully more efficient. That&amp;rsquo;s capacity that went straight back into higher‑value work instead of into overhead nobody had thought to question.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Developed a Data Analytics reporting system, increasing quarterly software revenue by 400% through Python‑based PDF reports.</title>
    <id>https://software.engineer.company/portfolio/developed-a-data-analytics-reporting-system-increasing-quarterly-44/</id>
    <link href="https://software.engineer.company/portfolio/developed-a-data-analytics-reporting-system-increasing-quarterly-44/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Analytics" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Stakeholder &amp; Reporting" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Analytics &amp; BI Dashboards" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <summary>Built a Python data-analytics reporting system that raised quarterly software revenue by 400% with clear, timely PDF reports.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Stakeholders weren&amp;rsquo;t getting analytics in any timely, readable form. The data existed, but turning it into something you could actually make a decision from was slow and manual, so visibility into how things were performing lagged, and the commercial decisions lagged with it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Building a data‑analytics reporting system — one that turned raw data into clear, regular insight without someone hand‑assembling it each time — was the job.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; A reporting system generated PDF reports in Python, automating the whole chain: pulling the data, running the analysis, and presenting it in a clean, consistent format stakeholders could actually read. The point was regularity and clarity — the same professional report landing predictably, so the numbers became something people looked at as a matter of course rather than something they had to go and dig for.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; That reporting is what drove quarterly software revenue up by 400%. Making the analytics better and faster wasn&amp;rsquo;t a back‑office nicety — put clear, timely numbers in front of the people making commercial decisions and the decisions get better, and here that showed up directly on the revenue.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Developed 100 web applications using HTML/HTML5, CSS/SCSS, Django, WordPress, and Joomla frameworks, ensuring diverse online presence.</title>
    <id>https://software.engineer.company/portfolio/developed-100-web-applications-using-html-html5-css-51/</id>
    <link href="https://software.engineer.company/portfolio/developed-100-web-applications-using-html-html5-css-51/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Developed 100 web applications with HTML5, CSS/SCSS, Django, WordPress and Joomla, matching each solution to the client&#39;s real needs.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Clients wanted web applications, and they wanted very different things — different scales, different budgets, different levels of &amp;ldquo;just get me online&amp;rdquo; versus &amp;ldquo;build me something custom.&amp;rdquo; Meeting that meant being versatile rather than forcing every client down the same technology.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Building web applications that gave each client a diverse, effective online presence was the job.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; A hundred web applications got built, and the point was matching the tool to the job rather than having a favourite. Where a client needed something custom, that was HTML/HTML5, CSS/SCSS and Django; where they needed something more standard that they could also manage themselves, WordPress or Joomla was the faster, more sensible answer. Part of the skill was knowing which was which — talking a client out of a bespoke build they didn&amp;rsquo;t need, or into one they did.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The hundred applications gave clients a varied, capable online presence, each delivered on the technology that actually fit it. Matching the approach to the client rather than the other way round is what made them effective rather than just delivered.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Designed 30 websites, delivering unique and flexible solutions by converting Photoshop designs to HTML.</title>
    <id>https://software.engineer.company/portfolio/designed-30-websites-delivering-unique-and-flexible-solutions-52/</id>
    <link href="https://software.engineer.company/portfolio/designed-30-websites-delivering-unique-and-flexible-solutions-52/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Designed 30 websites, converting Photoshop designs to HTML into distinctive, polished and maintainable web presences.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Clients came in with a look they wanted — often a finished visual design — and needed a website built faithfully from it. The gap between a design file and a working site is where a lot of quality is won or lost: it&amp;rsquo;s easy to ship something that&amp;rsquo;s roughly right and subtly wrong.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Designing the sites and converting the designs into accurate, flexible implementations was the job.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Thirty websites got designed and the Photoshop designs converted into HTML by hand. Faithful was the standard — the spacing, the type, the details the designer actually intended, not an approximation of them — but so was flexible, because a site that matches the mockup pixel‑for‑pixel and then falls apart the moment the content changes hasn&amp;rsquo;t really been built well. So the aim was markup that stayed true to the design and stayed maintainable afterwards.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The thirty sites matched their designs and stayed workable — distinctive, polished web presences that didn&amp;rsquo;t break the first time someone edited them. Faithful to the design and still maintainable is the balance that mattered, and that&amp;rsquo;s where these landed.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Administered 40 websites on Ubuntu Linux hosting servers with Apache and Nginx, ensuring high availability and performance.</title>
    <id>https://software.engineer.company/portfolio/administered-40-websites-on-ubuntu-linux-hosting-servers-53/</id>
    <link href="https://software.engineer.company/portfolio/administered-40-websites-on-ubuntu-linux-hosting-servers-53/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Linux &amp; Servers" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="System Administration" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <category term="System Administration" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Administered 40 websites on Ubuntu Linux with Apache and Nginx at high availability and performance — hosting clients never had to think about.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; There was a portfolio of live websites that needed to stay up and stay fast — and hosting is another of those jobs that&amp;rsquo;s invisible until a site goes down, at which point it&amp;rsquo;s the only thing anyone cares about.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Administering those sites and keeping them highly available and quick was the job.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Forty websites ran on Ubuntu Linux hosting servers, on a mix of Apache and Nginx — the configuration, the performance tuning, the ongoing maintenance to keep them reliable under real traffic. Real traffic is the operative bit: a site that&amp;rsquo;s fine when nobody&amp;rsquo;s using it and falls over when they are hasn&amp;rsquo;t been administered, it&amp;rsquo;s just been left alone. So the work was keeping them healthy under actual load.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; All forty ran with high availability and good performance, which gave clients hosting they didn&amp;rsquo;t have to think about. Stable and dependable under real use is the entire point of hosting, and that&amp;rsquo;s what these delivered.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Architected, developed, implemented, supported infrastructure, data processing, and the map application for 2 years non‑stop without any weekends, holidays, or vacations, 10–14 hours a day.</title>
    <id>https://software.engineer.company/portfolio/architected-developed-implemented-supported-infrastructure-data-processing-and-55/</id>
    <link href="https://software.engineer.company/portfolio/architected-developed-implemented-supported-infrastructure-data-processing-and-55/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="GIS / Geospatial" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="GIS &amp; Geospatial Solutions" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <summary>Architected, built and ran the infrastructure, data processing and map app for two years non-stop — the dependable backbone of the product.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; An early‑stage green‑energy startup depended on a single platform to track, monitor, and optimize renewable energy assets, yet had neither a dedicated infrastructure team nor an established engineering organization to build and operate it. The entire technical foundation — cloud infrastructure, data‑processing pipelines, and the customer‑facing GIS map application — had to be created and kept running continuously, in a market where any downtime or data gap directly eroded customer trust and revenue.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The task was to single‑handedly architect, build, and operate the whole system end to end, spanning platform and data engineering, DevOps, and site reliability. Beyond writing the software, this meant owning production: provisioning and hardening infrastructure, designing the data‑processing layer that fed the map, and guaranteeing the application stayed available around the clock for a growing customer base — all within the constraints and relentless pace of a fast‑moving startup.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; For two years the infrastructure, data pipelines, and map application were designed, implemented, and supported without interruption — no weekends, holidays, or vacations, often ten to fourteen hours a day. A pragmatic, modular architecture was chosen to keep a one‑person operation maintainable, with automated provisioning, monitoring, and alerting so issues could be detected and resolved quickly. Data processing was continuously tuned for reliability and performance, releases were shipped incrementally, and every layer — from servers to the user‑facing map — was personally maintained and improved in response to real customer usage.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The platform stayed continuously available and evolved from a fragile early prototype into the dependable backbone of the product, sustaining the company through its critical growth phase on the strength of a single engineer&amp;rsquo;s ownership. This hands‑on stewardship kept infrastructure, data, and the map application reliable enough to support upsells, data licensing, and new‑customer acquisition, and demonstrated a rare degree of commitment, breadth, and end‑to‑end accountability across the full stack.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Optimized budget costs 10 times with zero loss in productivity for the Saudi Arabia company by reimagining the overall infrastructure, eliminating unnecessary services, and relocating from the AWS cloud.</title>
    <id>https://software.engineer.company/portfolio/optimized-budget-costs-10-times-with-zero-loss-56/</id>
    <link href="https://software.engineer.company/portfolio/optimized-budget-costs-10-times-with-zero-loss-56/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Cloud" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Migrations &amp; Modernization" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Cloud Infrastructure &amp; Migration" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <summary>Cut infrastructure budget 10x with zero productivity loss for a Saudi company by re-imagining the stack and moving off AWS.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A company operating out of Saudi Arabia was carrying badly bloated infrastructure costs. Their AWS setup had been over‑provisioned and had collected services they no longer used, so the cloud bill had drifted completely out of proportion to what the business actually needed. It&amp;rsquo;s a common story — nobody sets out to overspend, it just accretes when no one&amp;rsquo;s watching the meter.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The engagement was to cut the costs substantially without losing any productivity, which meant rethinking the infrastructure properly rather than trimming round the edges — edge‑trimming rarely moves a bill that&amp;rsquo;s structurally too big.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; So the work went end to end. First came an audit of what was actually being used — which is where the duplicated and unnecessary services show themselves — and those got cut. Then whatever was left was right‑sized to match real demand instead of the worst‑case guesses the original setup had been built on. And the big move was relocating the workloads off AWS entirely, onto a more cost‑effective hosting arrangement — done carefully, in stages, so the running business never felt the migration happening underneath it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The budget costs fell roughly ten‑fold, with zero loss in productivity — the same capability at a fraction of what they&amp;rsquo;d been paying. It freed up a real amount of money that had quietly been leaking into an oversized cloud bill month after month, which for the business was money straight back to the bottom line.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Architected a layered maritime platform separating a Next.js PWA frontend, a Go (Huma/Fiber) API, and a PostgreSQL function layer, keeping all business logic in the database.</title>
    <id>https://software.engineer.company/portfolio/architected-a-layered-maritime-platform-separating-a-next-57/</id>
    <link href="https://software.engineer.company/portfolio/architected-a-layered-maritime-platform-separating-a-next-57/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <summary>Architected a layered maritime platform — Next.js PWA, a Go (Huma/Fiber) API and a PostgreSQL function layer holding all business logic.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; NextMariner was going to be a maritime platform — professional networking, company reviews, sponsored education — and it was a young product, which is a polite way of saying the requirements were going to move around a lot. The thing to avoid was an architecture where changing a business rule meant touching the frontend, the API and the database all at once. On a small codebase that grows fast, that kind of coupling is what turns a two‑line change into an afternoon.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; As the architect, the call to make up front was where each kind of logic lived, with boundaries obvious enough to hold up under pressure instead of blurring the first time someone was in a hurry.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; It settled into three layers, one job each. The Next.js frontend does presentation and interactivity and nothing else. The Go API — Huma over Fiber — is deliberately thin: it routes, validates the request, applies the security filtering and orchestrates, but it holds no business logic at all. The business logic all lives in PostgreSQL functions, which assemble the full result and hand it back for the API to forward. So when a rule changes, it changes in one layer, in SQL, and the other two don&amp;rsquo;t need to know. The boundaries were written down and then enforced in review, because a convention nobody polices stops being one.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The thing stayed easy to hold in your head. Business logic sits in one place you can actually audit, the API is a boring adapter in the good sense, and the frontend doesn&amp;rsquo;t care when the schema shifts underneath it. That separation is what let the product keep bolting on features without the architecture quietly rotting.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Designed a JSON passthrough architecture where PostgreSQL functions return complete JSON forwarded verbatim by the Go API, eliminating intermediate unmarshalling and decoupling the frontend from schema changes.</title>
    <id>https://software.engineer.company/portfolio/designed-a-json-passthrough-architecture-where-postgresql-functions-58/</id>
    <link href="https://software.engineer.company/portfolio/designed-a-json-passthrough-architecture-where-postgresql-functions-58/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <summary>Designed a JSON passthrough where PostgreSQL functions return complete JSON forwarded verbatim by the Go API, decoupling the frontend from schema.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The usual way data gets from a database to a browser is a relay race of transformations. The database hands the API rows, the API unmarshals them into structs, reshapes them, serialises them back to JSON, and only then do they go out. Every one of those hops is code you write, code you test, and one more place where the API&amp;rsquo;s idea of the data and the database&amp;rsquo;s idea of it can drift apart.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The idea was to skip the relay race. If the database could return the finished response, the API could just pass it along, and the frontend could depend on the database&amp;rsquo;s shape directly instead of on a hand‑maintained copy of it living in Go.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; So it was built as a straight passthrough. The PostgreSQL functions assemble the whole response as JSON — the shaping is a SQL concern, done where the data already is. The Go handler takes that back as json.RawMessage and forwards it untouched; it never decomposes it, never re‑encodes it. A small QueryJSON helper made that pattern the path of least resistance rather than something you had to remember to do. What fell out was all the intermediate machinery a conventional layered API collects — the DTOs, the mappers, the response structs.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Handler code got dramatically shorter, and more to the point the frontend stopped being coupled to Go. Change what a function returns and the new shape flows straight through to the client without anyone editing a line of handler code. Fewer moving parts, and one whole category of layer‑to‑layer drift simply doesn&amp;rsquo;t exist here.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Designed an organization context‑switching system with client localStorage and server‑side cookie mirroring, letting users act as managed organizations while enforcing least‑privilege authorization.</title>
    <id>https://software.engineer.company/portfolio/designed-an-organization-context-switching-system-with-client-59/</id>
    <link href="https://software.engineer.company/portfolio/designed-an-organization-context-switching-system-with-client-59/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Security &amp; Access Management" scheme="https://software.engineer.company/services/" />
    <summary>Built organization context-switching with localStorage and server cookie mirroring, letting users act as managed orgs under least-privilege auth.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; On NextMariner a maritime professional can manage organizations — companies, academies — that don&amp;rsquo;t have their own logins. The person is the account; the organization is something they act on behalf of. So a user needs to move through the whole app as any organization they manage, switching between them freely, and that convenience can&amp;rsquo;t turn into a hole in the authorization.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Context‑switching had to be quick and unobtrusive for the user while making sure the active context could never, on its own, hand someone access they weren&amp;rsquo;t entitled to.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; An OrganizationContext handles it, with storage on both sides. On the client, localStorage is the source of truth for which organization you&amp;rsquo;re currently acting as, so switching is instant — no round‑trip. A server‑side cookie mirrors it so that server‑rendered pages resolve the same context during SSR; there&amp;rsquo;s a getServerViewMode on the server that reads it. The important part is that none of that is trusted for access decisions. Authorization gets re‑checked on the server on every request. The frontend context is there for the experience — showing you the right thing — and the server is the only authority on what you&amp;rsquo;re allowed to do.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; A user can act as any organization they manage without friction, and the interface stays in sync on both client and server. But because permissions are verified server‑side every time, none of that convenience weakens the security. Someone tampering with what&amp;rsquo;s in localStorage changes what their own UI shows them and nothing more — the server still says no.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Migrated the HTTP API from Fiber to Huma v2 — 649 paths and 760 operations — reaching and holding 100% parity between the routes the server registers and the OpenAPI description it publishes.</title>
    <id>https://software.engineer.company/portfolio/migrated-the-http-api-from-fiber-to-huma-60/</id>
    <link href="https://software.engineer.company/portfolio/migrated-the-http-api-from-fiber-to-huma-60/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Migrations &amp; Modernization" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Technical Documentation" scheme="https://software.engineer.company/services/" />
    <summary>Migrated the HTTP API from Fiber to Huma v2 — 649 paths, 760 operations — at 100% parity between registered routes and the published OpenAPI description.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The API started life on Fiber, with request validation written out by hand, endpoint by endpoint. That&amp;rsquo;s fine when there are a handful of endpoints. It stops being fine as the surface grows: the hand‑rolled validation turns into a maintenance tax, and small inconsistencies creep in because every endpoint&amp;rsquo;s checks are their own little snowflake. And there was no single description of the API&amp;rsquo;s shape anywhere.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The goal was validation coming from the types instead of from hand‑written checks, and an actual contract describing the API — without stopping to do a big‑bang rewrite.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The HTTP layer moved onto Huma v2, sitting on top of Fiber, so the existing runtime stayed. Each endpoint gets input and output structs, and Huma generates the request validation and response modelling from those types. An OpenAPI description comes out of it for free, which means the documentation tracks the code instead of rotting in a wiki. Everything new got written against Huma and the existing routes migrated over, with exactly two endpoints left on raw Fiber — the WebSocket ones, where you genuinely want the socket and Huma&amp;rsquo;s request/response model doesn&amp;rsquo;t fit. What that has grown into is 649 paths carrying 760 operations, and a check in CI that compares the routes the server actually registers against the ones the OpenAPI description advertises. Parity is 100%, and it stays there because a route that isn&amp;rsquo;t described fails the build.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; New endpoints get validation and current documentation without anyone doing extra work for it, and a whole class of request‑handling bugs — the &amp;ldquo;oh, we forgot to check that field here&amp;rdquo; kind — went away. At 760 operations the description is the only practical way anyone reads the API, so the guarantee that it is complete matters more than it did at fifty. The typed contract made the API both safer to change and easier to hand to someone else, because the types tell you what an endpoint expects.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Generated the API contract outward from the database — OpenAPI, a 44,076‑line typed TypeScript client, 61 mock handlers and the limits the UI enforces — with a guard at every hop that fails on drift.</title>
    <id>https://software.engineer.company/portfolio/built-a-request-schema-validation-contract-with-automated-61/</id>
    <link href="https://software.engineer.company/portfolio/built-a-request-schema-validation-contract-with-automated-61/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <summary>Generated the API contract from the database outward — OpenAPI, a typed TypeScript client, mock handlers and UI limits — with a guard at every hop.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Frontend and backend move at their own pace, and their assumptions about a request payload can drift apart without anyone noticing. The way you usually find out is a 422 in the browser — after the mismatch has already shipped, which is the most expensive moment to learn about it. Writing the two sides by hand from the same document does not fix it; it just moves the drift to whoever forgot to reread the document.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The two sides had to be generated from one source rather than agreed between two, with every step of the generation checked instead of trusted.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The chain starts at the database and runs outward. The schema and its functions define the shapes; the Go types define the API; Huma emits the OpenAPI description from those; a typed TypeScript client — 44,076 lines of it — is generated from that description; 61 mock handlers are generated alongside it so the frontend&amp;rsquo;s own tests run against the real contract rather than a hand‑written fixture; and the limits the UI enforces on a form come from the same place instead of being retyped into a validator. Every hop has a guard. A contract check in CI compares what the frontend sends against what the API expects and fails the build on divergence, with a schema probe underneath it that checks the real shapes rather than a description of them. It deliberately covers where drift likes to hide: optional body fields, where &amp;ldquo;missing&amp;rdquo; and &amp;ldquo;null&amp;rdquo; get confused, and query‑parameter enums, where the two sides can quietly disagree on the allowed values.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; A field cannot change on one side only — it fails at the first hop that notices, in a build, minutes after the change. That took out a recurring and genuinely annoying class of bug, the kind invisible in code review that only shows up at runtime. The cost is a generation step in the middle of everything: regenerating is a chore, and the chain is only as trustworthy as its least‑guarded link, which is why each hop got one.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built email as a platform capability — three providers with failover, delivery webhooks, send and delivery logging, templating and campaigns — behind a startup check that will not boot without one.</title>
    <id>https://software.engineer.company/portfolio/built-email-as-a-platform-capability-with-failover-62/</id>
    <link href="https://software.engineer.company/portfolio/built-email-as-a-platform-capability-with-failover-62/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Cloud" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Security &amp; Access Management" scheme="https://software.engineer.company/services/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <summary>Built email as a platform capability: three providers with failover, delivery webhooks, send and delivery logging, templating and campaign broadcasts.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Email carries a lot of weight on NextMariner — verification, notifications, digests, campaigns, the things a user actually waits for. And a mail provider is exactly the kind of dependency that fails quietly: the config looks fine, the app boots, and you only find out something&amp;rsquo;s broken when a real person never gets the message they were promised. That&amp;rsquo;s the worst way to learn about it. One provider makes it worse, because the failure is total and someone else&amp;rsquo;s to fix.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Email had to be treated as a capability the platform owns rather than a client library it calls — able to survive a provider outage, able to say what happened to a given message, and loud at startup in the environments where silence is dangerous.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Three providers sit behind one interface — SendGrid as primary, with SMTP2GO and Azure Communication Services behind it — and failover between them is automatic rather than a configuration change made under pressure. Delivery is not assumed: inbound webhooks report what each provider did with a message, and both sides are recorded, in a send log and a delivery event table, so &amp;ldquo;did this person get their verification mail&amp;rdquo; is a query rather than a guess. Templating keeps the message bodies out of the code, and a separate broadcast schema — 5 tables and 24 functions — carries campaigns to segments of users, which is a different problem from transactional mail and was built as one. In front of all of it, a startup check sends a real message through the stack, behind a flag: in development it logs a warning and carries on, because nobody wants their laptop refusing to start over an expired sandbox key, and in staging and production a failure is fatal and the process exits rather than deploy a build that cannot send mail. The send path itself goes through an SSRF‑protected client with a 30‑second timeout, and the async delivery path has retries and backoff so a momentary blip doesn&amp;rsquo;t drop a message.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; A whole category of silent failure moved from &amp;ldquo;a user notices days later&amp;rdquo; to &amp;ldquo;the deploy stops&amp;rdquo;, and a provider having a bad afternoon became a degraded path rather than an outage. The cost is three integrations to keep working instead of one, and delivery logs that grow and have to be pruned — both accepted, because email is the channel the platform cannot route around.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Designed a PostgreSQL function‑first data layer — 1,275 stored functions across 34 schemas — so every read and write goes through a function the database can grant, rather than through a table.</title>
    <id>https://software.engineer.company/portfolio/designed-a-postgresql-function-first-data-layer-across-63/</id>
    <link href="https://software.engineer.company/portfolio/designed-a-postgresql-function-first-data-layer-across-63/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <summary>Designed a PostgreSQL function-first data layer — 1,275 stored functions across 34 schemas — with the access boundary enforced by the database itself.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Business logic has a way of leaking. A bit ends up in the API, a bit in some SQL a handler runs inline, and before long the same rule is written two or three slightly different ways and there&amp;rsquo;s nowhere you can point to and say &amp;ldquo;this is what the system does with its data.&amp;rdquo; That&amp;rsquo;s how bugs and security holes get in.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The goal was one home for all of it: every read and write going through the database, the API a thin adapter that doesn&amp;rsquo;t know the business rules, and the whole thing lockable down tight.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The data layer is function‑first. The schema is split by domain — identity, organization, review, message, notification and 29 more, 34 in all — and every operation the app can do is one of 1,275 PostgreSQL functions it calls; there&amp;rsquo;s no direct table access from Go at all. Then the database enforces it. The role the API logs in as, mariner, has EXECUTE on the app functions and USAGE on the schemas and nothing else — no SELECT, no INSERT, no way to touch a table directly — which comes to roughly 4,000 explicit grants rather than a blanket one. The functions run SECURITY DEFINER, owned by a separate non‑login function_owner role with a pinned search_path, and the superuser account stays reserved for migrations and cron, well away from the running app.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The logic lives in one place you can actually audit, the API stays thin and boring in the good way, and the access boundary is enforced by Postgres itself rather than by everyone remembering the rules. If the API were somehow compromised, it still couldn&amp;rsquo;t do anything the functions don&amp;rsquo;t allow. At 1,275 functions the discipline costs something real — a new field is a migration and a function change, not a line in a query — and that friction is the price of the boundary holding.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Implemented a catalogue‑driven deep‑merge for stored JSON preferences, preventing missing‑key crashes as the schema evolves.</title>
    <id>https://software.engineer.company/portfolio/implemented-a-catalogue-driven-deep-merge-for-stored-65/</id>
    <link href="https://software.engineer.company/portfolio/implemented-a-catalogue-driven-deep-merge-for-stored-65/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Migrations &amp; Modernization" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <summary>Implemented a catalogue-driven deep-merge for stored JSON preferences, preventing missing-key crashes as the schema evolves.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The platform stores JSON preferences — notification settings and the like — as a user&amp;rsquo;s saved values laid over a set of defaults. The original merge did that at a single level. The problem shows up later: add a new key to the defaults, and rows saved before that key existed simply don&amp;rsquo;t have it. Then some client code reads that field, gets undefined, and falls over — for exactly the users who&amp;rsquo;ve been around longest.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Evolving the preference schema had to be safe, so that adding a setting could never break the people who signed up before it existed.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The single‑level merge was replaced with a deep‑merge driven by the defaults as a catalogue. The defaults are treated as the authoritative list of every key that should exist; the user&amp;rsquo;s stored values are merged recursively on top, so anything in the catalogue is guaranteed to come out present, whether or not it was in the saved blob. Add a key to the defaults and it appears in every existing row&amp;rsquo;s effective preferences automatically, nested keys included. It rolled in through a migration so existing data got the benefit straight away rather than waiting to be rewritten.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Preferences can grow without fear. Adding a setting no longer risks an undefined‑field crash in the client, and the frontend stopped needing defensive checks scattered around every place it reads a preference. It also gave the rest of the platform a dependable way to extend any stored‑JSON blob — the pattern, not just the one fix.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the Python vessel‑data scrapers (MarineTraffic, Maritime‑Database) and a repeatable import that seeds the platform&#39;s reference data — 184,197 rows, including 698 companies and 56,149 vessels.</title>
    <id>https://software.engineer.company/portfolio/built-a-python-vessel-data-scraper-marinetraffic-maritime-66/</id>
    <link href="https://software.engineer.company/portfolio/built-a-python-vessel-data-scraper-marinetraffic-maritime-66/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <summary>Built Python vessel-data scrapers and seeded 184,197 rows of maritime reference data — 698 companies and 56,149 vessels — through a repeatable pipeline.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A maritime networking‑and‑reviews site is dead on arrival if it&amp;rsquo;s empty. Nobody joins a directory with no companies in it. So before any of the social features mattered, the platform needed a real body of maritime companies and vessels already sitting there, ready to be found.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Go and get that data — real companies and ships, at enough scale to feel populated — and get it into the database in a way that could be rerun, not a one‑off scrape nobody could reproduce.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The scrapers are written in Python. One drives MarineTraffic with Playwright; another pulls from Maritime‑Database over async httpx; there&amp;rsquo;s a ClassNK fetcher in there too. They write out CSVs, and an import step cleans and normalises those and loads them into the Postgres schema through a single task, so seeding the database is one command rather than an afternoon of manual work. What went in came to 184,197 rows: 698 companies, 56,149 vessels and 33,074 cities, with a later refresh replacing 74,794 vessel rows.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The platform launched with a populated directory instead of empty tables, and a base of reference data the networking, jobs and review features could all build on top of. Because the pipeline is repeatable, refreshing or extending it later is just running it again — which is how the vessel refresh happened without anyone rebuilding the tooling. Scraped data ages, and keeping it current is an ongoing cost rather than a solved problem.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Modeled the maritime domain into 348 normalized tables across 34 PostgreSQL schemas — professionals, companies, ships, jobs, reviews and the rest — with SMALLINT lookups and UUID v7 keys.</title>
    <id>https://software.engineer.company/portfolio/modeled-the-maritime-domain-professionals-companies-ships-jobs-67/</id>
    <link href="https://software.engineer.company/portfolio/modeled-the-maritime-domain-professionals-companies-ships-jobs-67/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <summary>Modeled the maritime domain into 348 normalized tables across 34 PostgreSQL schemas, with SMALLINT lookups, UUID v7 keys and one naming convention.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The heart of NextMariner is a dense maritime domain — professionals, companies, ships, jobs, reviews — and these things reference each other constantly. A professional sails on ships, works for companies, leaves reviews; a company owns ships and posts jobs. Nearly every feature is a query across that web, so how well the data is modelled decides how well most of the app performs and how sane it is to extend.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; That domain had to be modelled so it stayed fast and kept its integrity, and so that adding the next entity type didn&amp;rsquo;t mean fighting the schema.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; It&amp;rsquo;s laid out as normalized PostgreSQL schemas organised by domain — 348 tables across 34 of them by now. The many small, stable enumerations — statuses, types, categories — became SMALLINT lookup tables, which keeps the rows compact and the joins cheap instead of storing text codes everywhere. Entities get UUID v7 primary keys, so they&amp;rsquo;re globally unique but still time‑ordered in the index. One naming convention runs throughout — plural table names inside singular‑named schemas — applied without exception, which as a bonus sidesteps a lot of reserved‑word collisions. And the relationships are held up by real foreign keys and constraints, so integrity is the database&amp;rsquo;s job, not something the application has to remember to do.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; What came out is a data model that&amp;rsquo;s consistent and quick, and predictable to work in because the same rules hold everywhere — there aren&amp;rsquo;t special cases to memorise. Adding a feature usually means extending the schema along the existing grain rather than working against it, which is the only reason 34 schemas is a structure rather than a sprawl. It&amp;rsquo;s the floor the rest of the platform stands on.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the Next.js 16 frontend with deliberate SSR, SSG and CSR strategies, and a reusable prefetched‑server‑page factory that removes the N&#43;1 fetch cascade from each authenticated page moved onto it.</title>
    <id>https://software.engineer.company/portfolio/built-the-next-js-16-frontend-with-deliberate-73/</id>
    <link href="https://software.engineer.company/portfolio/built-the-next-js-16-frontend-with-deliberate-73/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Built the Next.js 16 frontend with SSR/SSG/CSR and a server-shell prefetch-and-hydrate pattern that eliminated N+1 fetches.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The authenticated dashboard needed to feel quick and stay genuinely interactive, and those two goals pull against each other if you&amp;rsquo;re naïve about it. Fetch everything on the client and the first load drags, and worse, you get the N+1 pattern where every component wakes up and fires its own request, so a single page turns into a cascade of round‑trips.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Each part of the app needed rendering the way that actually suited it, without giving up the client‑side interactivity where it mattered.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The Next.js 16 frontend uses the right mode per surface instead of one blanket choice. Marketing and public pages are statically generated — they don&amp;rsquo;t change per user, so there&amp;rsquo;s no reason to render them on every request. The genuinely interactive parts stay client‑rendered. And where the N+1 cascade actually bites — the authenticated dashboard and the big directory listings — the fix was factored rather than hand‑rolled: createPrefetchedServerPage fetches the page&amp;rsquo;s data on the server and hands it to the client already populated, so the components come up with their data instead of each going off to ask for it. A cache‑invalidation strategy is documented per query, so data stays fresh without the app re‑fetching things it already has.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The pages moved onto the factory come up in one round‑trip instead of a storm of them, and because it is a factory rather than a pattern people copy by hand, the next page moved over inherits the behaviour for free. The rest of the authenticated app is still client‑rendered and still queued behind it — a migration with a working mechanism and an obvious next page, rather than a finished sweep.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Delivered full Progressive Web App support — installable and offline‑capable — with Workbox runtime caching via next‑pwa.</title>
    <id>https://software.engineer.company/portfolio/delivered-full-progressive-web-app-support-installable-and-74/</id>
    <link href="https://software.engineer.company/portfolio/delivered-full-progressive-web-app-support-installable-and-74/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Delivered full Progressive Web App support — installable and offline-capable — with Workbox runtime caching via next-pwa.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A lot of NextMariner&amp;rsquo;s users are at sea. Seafarers and field staff, on phones, frequently offline or hanging off a bad connection — not people sitting at a desk on reliable office wifi. Building as if everyone had a fast, constant network would have quietly excluded a big part of the actual audience.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The app had to be installable like a native one, and still usable when the network drops out.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; It&amp;rsquo;s delivered as a full Progressive Web App. It installs to the home screen with proper icons across the various sizes, a splash screen and theme colours, so it looks and launches like an app rather than a bookmark. The service worker is set up through the next‑pwa plugin, and Workbox runtime caching uses CacheFirst for the things that don&amp;rsquo;t change per request — fonts, images, audio, video, CDN assets — alongside sensible HTTP cache‑control headers. Because service workers only really behave over HTTPS, HTTPS‑based testing validated the offline and install behaviour on real devices instead of hoping it worked.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Users can install the app and keep using it offline, and repeat loads come back fast from cache instead of over the wire. The platform behaves like a native app on the hardware its users actually carry, which for this audience isn&amp;rsquo;t a nice‑to‑have — it&amp;rsquo;s the difference between the app being usable at sea and not.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Designed an iOS 26 &#39;Liquid Glass&#39; design system and a canonical component inventory enforced by lint rules to prevent UI divergence.</title>
    <id>https://software.engineer.company/portfolio/designed-an-ios-26-liquid-glass-design-system-75/</id>
    <link href="https://software.engineer.company/portfolio/designed-an-ios-26-liquid-glass-design-system-75/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>Designed an iOS 26 &#39;Liquid Glass&#39; design system and a canonical component inventory enforced by lint rules to stop UI divergence.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A UI without a shared visual language and a fixed set of components drifts, and it drifts fast. Every new screen reinvents its own buttons and badges and cards, each a little different, and those small inconsistencies pile up until the product looks incoherent and every change means touching five bespoke versions of the same thing.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The design system had to be coherent enough to look deliberate, and enforceable enough that it wouldn&amp;rsquo;t erode the moment the team was busy.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The visual language and the component system were designed as one thing. The language is an iOS 26 &amp;ldquo;Liquid Glass&amp;rdquo; look — translucent panels with backdrop blur, layered shadows, a bit of refraction, spring animations — built out of Tailwind utilities. On top of it sits a canonical set of components every screen is supposed to compose from: Card, Label, Button, GlassIconButton, DirectoryGrid and the rest. The part that makes it stick is the linting: rules that reject a hand‑rolled badge or chip and point you at the canonical component instead, so the system is held up by tooling rather than by whoever&amp;rsquo;s reviewing that day remembering to care. And the docs are paired with concrete mistake‑to‑resolution notes, so the guidance is &amp;ldquo;here&amp;rsquo;s the wrong way and the right way,&amp;rdquo; not an abstract principle.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The UI stays consistent and on‑brand, and the one‑off components that would otherwise spread get caught before they do. Visual consistency stopped being a matter of everyone&amp;rsquo;s discipline and became something the tooling holds the line on, which is the only version of it that survives a deadline.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Designed the product&#39;s UI and UX end to end — directory grids, dual card/table views, live requirement validators and breadcrumb navigation.</title>
    <id>https://software.engineer.company/portfolio/designed-the-product-s-ui-and-ux-end-76/</id>
    <link href="https://software.engineer.company/portfolio/designed-the-product-s-ui-and-ux-end-76/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>Designed the product&#39;s UI/UX end to end — directory grids, card/table views, live validators and breadcrumbs — for dense maritime data.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; NextMariner puts a lot of different entities in front of people — professionals, companies, ships, jobs, reviews — and the interface had to be two things that fight each other: good‑looking and genuinely usable. Dense enough to show real maritime data, but not so dense it turns into a wall you bounce off.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The product&amp;rsquo;s UI and UX were owned end to end — the layouts, the interaction patterns, and the smaller stuff like how someone gets walked through a form without feeling nagged.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; A few decisions did most of the work. The directory grids have a card/table toggle, so you can browse visually or scan a dense table, and the app remembers which you picked. Forms use live requirement validators that sit above the input and show each rule in green when it&amp;rsquo;s met and amber when it isn&amp;rsquo;t, so you&amp;rsquo;re guided while you type instead of scolded after you submit. Navigation is consistent breadcrumbs and two‑column entity layouts, so pages feel like the same product rather than a set of unrelated screens. And the error philosophy is guidance over failure — no red walls, redirects instead of dead ends, the app trying to keep you moving rather than stopping you.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; What came out is a polished, consistent experience that makes dense maritime data approachable and steers people through the complicated parts. It reads as considered and trustworthy, which isn&amp;rsquo;t cosmetic on a platform people are using for their actual careers — if it looked slapdash, they&amp;rsquo;d trust the data less, and they&amp;rsquo;d be right to.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Hardened the application with nonce‑based CSP, HSTS, SameSite cookies, least‑privilege database roles and server‑side entitlement re‑checks.</title>
    <id>https://software.engineer.company/portfolio/hardened-the-application-with-nonce-based-csp-hsts-77/</id>
    <link href="https://software.engineer.company/portfolio/hardened-the-application-with-nonce-based-csp-hsts-77/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Security &amp; Access Management" scheme="https://software.engineer.company/services/" />
    <summary>Hardened the app with nonce-based CSP, HSTS, SameSite cookies, least-privilege database roles and server-side entitlement re-checks.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; NextMariner holds professional and organizational data, the kind people expect to be handled properly, so a single line of defence was never going to be enough. The working assumption has to be that the client is hostile — that anything the browser enforces can be switched off by whoever&amp;rsquo;s holding the browser — and the security has to hold up anyway.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The platform had to be hardened at every layer — frontend, API, database — so that security was enforced by the server independently of whatever the interface happened to allow.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; On the frontend, the Next.js proxy middleware — proxy.ts — sets a Content‑Security‑Policy with a per‑request nonce and strict‑dynamic, plus HSTS and SameSite cookies, so the browser is locked down about what it&amp;rsquo;ll run and send. At the API, there&amp;rsquo;s rate limiting, CORS, request‑size limits, input validation before anything touches the database, and logging of the security‑relevant events. In the database, the API logs in as a least‑privilege role that can only EXECUTE the app functions, the functions run SECURITY DEFINER, and everything is parameterised. And the entitlements — tier, role, organization, ship scoping — are re‑checked on the server on every request, with the frontend gates treated as UX only. The gates decide what you see; the server decides what you can do.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Security doesn&amp;rsquo;t depend on the UI behaving. The protections are layered so that getting past one doesn&amp;rsquo;t get you past the rest, and the whole thing is built on the assumption that the client can&amp;rsquo;t be trusted — which is the right assumption for data people are handing over in confidence.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Established an English/Danish internationalization system carrying 12,027 messages per locale across 458 namespace files, with linter‑enforced vocabulary and a 400‑line budget per file.</title>
    <id>https://software.engineer.company/portfolio/established-an-english-danish-internationalization-system-with-linter-78/</id>
    <link href="https://software.engineer.company/portfolio/established-an-english-danish-internationalization-system-with-linter-78/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <summary>Established an English/Danish i18n system carrying 12,027 messages per locale across 458 files, with linter-enforced vocabulary and a size budget.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; NextMariner runs in English and Danish, and translation systems have a way of sprawling into a mess. The files grow without limit, keys leak — present in one locale, missing in the other — and the language‑specific conventions get applied unevenly, so one locale ends up reading like it was translated by a committee that didn&amp;rsquo;t talk to each other. For a professional product that&amp;rsquo;s not a small blemish; it reads as carelessness.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The internationalization setup had to scale — keeping both languages consistent and correct, and the files something a person could still maintain a year in.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; It&amp;rsquo;s built on next‑intl with rules the tooling actually enforces. The Danish side has a locked vocabulary and style — literal æøå, the informal &amp;ldquo;du&amp;rdquo;, the right imperative accents, and semantic mappings for compound nouns so they&amp;rsquo;re translated by meaning rather than word‑for‑word — and a linter holds it to that. There&amp;rsquo;s a hard 400‑line budget per namespace file, with a split‑and‑merge approach so a big area divides into nested files that merge back cleanly instead of one file growing forever; that budget is why 12,027 messages per locale live in 458 files rather than a handful of enormous ones. A linter check compares keys across locales so nothing leaks or goes missing. And stored overrides are merged catalogue‑driven rather than with fragile top‑level fallbacks — the same deep‑merge idea used for preferences.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Both locales stay correct and consistent at 24,054 messages between them, and the files stay maintainable as the string count climbs. Language quality became something the tooling guarantees on every commit rather than something that quietly degrades each time someone adds a string in a hurry. The budget is the load‑bearing part: without a limit per file, 458 files would have been twelve, and nobody would open them.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Set the platform&#39;s founding decisions in the weeks after the repository opened in November 2025 — the layering, database‑first data access and the zero‑warnings bar — and they still hold nine months on.</title>
    <id>https://software.engineer.company/portfolio/set-the-platform-s-founding-decisions-in-november-2025-80/</id>
    <link href="https://software.engineer.company/portfolio/set-the-platform-s-founding-decisions-in-november-2025-80/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Team Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Leadership" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>Set the platform&#39;s founding decisions in November 2025 — the layering, database-first data access and the zero-warnings bar — and they still hold.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The repository opened on 25 November 2025 with nothing in it. What gets decided in the first few weeks of a project like that is disproportionate: the layering, where the business logic is allowed to live, what the quality bar is. Those choices are cheap to make on day three and close to impossible to reverse by month six, by which point everything written since assumes them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The founding decisions had to be made deliberately and early, and made in a form that could survive being handed to other people and to a much larger codebase than existed at the time.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Three decisions did most of the work. The stack was layered so each part has one job — a Next.js frontend, a Go API that validates and forwards, a PostgreSQL function layer that owns the business rules — rather than logic being placed wherever it was convenient that afternoon. Data access was put behind stored functions from the start, which is the choice everything else in the database record follows from; retrofitting it later would have meant rewriting every handler. And a zero‑warnings bar went in before there was much code to hold to it, because a standard introduced at commit 5,000 is a cleanup project, whereas the same standard at commit 50 is just how the repository works. None of the three were the easy option at the time, and all three cost momentum in the first month.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Nine months and several thousand commits later, all three still hold: the layers have not blurred, no handler talks to a table directly, and the build still has no warnings in it. That is the check worth applying to a founding decision — not whether it sounded right, but whether it survived contact with the volume of work that came after, which is the point at which convenient choices usually get quietly abandoned.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Developed and maintained Django web applications for the IPTV platform, shipping new features and improving performance and stability.</title>
    <id>https://software.engineer.company/portfolio/developed-and-maintained-django-web-applications-for-the-84/</id>
    <link href="https://software.engineer.company/portfolio/developed-and-maintained-django-web-applications-for-the-84/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Developed and maintained Django web applications for an IPTV platform, shipping features while improving performance and stability.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The IPTV platform had web applications built on Django around it — the parts people actually clicked on — and they needed to keep moving forward: new features, and the performance and stability that a platform running around the clock demands.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Keeping those applications feature‑rich, fast and stable was the task.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The Django web applications were developed and maintained — building new features, fixing the bugs, and pushing on performance and stability, worked across the team rather than in a corner. On something serving customers continuously, the stability side isn&amp;rsquo;t a nice‑to‑have alongside the features; it&amp;rsquo;s the constraint the features have to respect. A flashy feature that makes the thing wobble isn&amp;rsquo;t worth much when the thing can&amp;rsquo;t afford to wobble.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The applications kept gaining capability while staying reliable for both the internal teams and the customers. Adding to something without destabilising it is the balance that mattered here, and that&amp;rsquo;s what the work held to.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Planned and implemented new infrastructure functionality for internal and external systems, building solutions durable enough to still run years later with minimal change.</title>
    <id>https://software.engineer.company/portfolio/planned-and-implemented-new-infrastructure-functionality-for-internal-85/</id>
    <link href="https://software.engineer.company/portfolio/planned-and-implemented-new-infrastructure-functionality-for-internal-85/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="System Administration" scheme="https://software.engineer.company/categories/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <category term="System Administration" scheme="https://software.engineer.company/services/" />
    <summary>Planned and built infrastructure for internal and external systems durable enough to still run years later with minimal change.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; As the organisation grew, its internal and external systems kept needing new capabilities bolted on. The easy way to do that is whatever&amp;rsquo;s quickest today; the trouble with the easy way is you&amp;rsquo;re back fixing it in six months.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Planning and building infrastructure functionality that would actually last was the task — not just work now, but keep working.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; New infrastructure functionality was planned and implemented across the internal and external systems, designed to be durable — the kind of thing you build once, properly, so it keeps running for years with minimal touching rather than needing constant attention. That&amp;rsquo;s a deliberate choice each time: spend a bit more thought up front so you&amp;rsquo;re not signing yourself up to babysit it forever.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The systems stayed functional and effective long after they were built, running for years with barely any change. That longevity is the real measure of infrastructure work — anyone can make something that works today; making something that&amp;rsquo;s still quietly working years later is the harder and more useful thing.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>As one of the first hires, designed and built the entire core infrastructure and supporting processes from scratch for a green‑energy SaaS startup, laying the foundation for rapid growth.</title>
    <id>https://software.engineer.company/portfolio/as-one-of-the-first-hires-designed-and-86/</id>
    <link href="https://software.engineer.company/portfolio/as-one-of-the-first-hires-designed-and-86/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Cloud" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="System Administration" scheme="https://software.engineer.company/categories/" />
    <category term="Cloud Infrastructure &amp; Migration" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="System Administration" scheme="https://software.engineer.company/services/" />
    <summary>As one of the first hires, designed and built a green-energy SaaS startup&#39;s entire core infrastructure from scratch, enabling rapid growth.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; This was a green‑energy SaaS startup with a promising idea and essentially no technical foundation under it yet. Coming in as one of the first hires meant the stage where there&amp;rsquo;s nothing to maintain because nothing exists — building the ground everyone else will stand on.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Building the core infrastructure and the processes around it, from scratch, was the job.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The entire core infrastructure and its supporting processes were designed and built — the servers, the networks, the data flow, the security, the operations side. Doing that at a startup means making decisions that are hard to unmake later, so the goal wasn&amp;rsquo;t just &amp;ldquo;get something running,&amp;rdquo; it was to lay a foundation that could take the weight of rapid growth without needing to be ripped out the moment the company got bigger. Early infrastructure choices either become the thing that lets you scale or the thing you spend a year undoing; the aim was firmly the first kind.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The startup came away with a solid technical foundation, and it&amp;rsquo;s what let the business grow quickly afterward. Being the person who builds that base from nothing is a particular kind of responsibility — get it right and nobody notices, get it wrong and everyone does — and this one held up.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Maintained and enhanced the legacy Hugo static‑site website while contributing UI/UX improvements to the primary asset‑management product.</title>
    <id>https://software.engineer.company/portfolio/maintained-and-enhanced-the-legacy-hugo-static-site-87/</id>
    <link href="https://software.engineer.company/portfolio/maintained-and-enhanced-the-legacy-hugo-static-site-87/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Maintained and improved a legacy Hugo static website while contributing UI/UX refinements to the core asset-management product.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; There was a legacy website built on Hugo — a static‑site generator — running alongside the company&amp;rsquo;s main product, an asset‑management system. The website was the old, established thing; the product was where the real value sat.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Keeping the site healthy while also improving the product&amp;rsquo;s experience was the remit.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The Hugo static site was maintained and enhanced — kept current and working — while UI/UX improvements and feedback fed into the primary asset‑management product at the same time. Splitting attention between a legacy site and the flagship product is mostly about not letting the old thing rot while you&amp;rsquo;re focused on the new one; both represent the company to someone, so both had to be kept decent.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The website stayed current instead of quietly ageing, and the main product&amp;rsquo;s usability improved through steady, informed refinement — the kind that comes from actually using and thinking about the thing rather than redesigning it in one big swing.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Drove client web success by combining custom website development and design with SEO, content strategy and copywriting.</title>
    <id>https://software.engineer.company/portfolio/drove-client-web-success-by-combining-custom-website-91/</id>
    <link href="https://software.engineer.company/portfolio/drove-client-web-success-by-combining-custom-website-91/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Brand &amp; Marketing" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Drove client web success by combining custom website development and design with SEO, content strategy and copywriting.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Clients didn&amp;rsquo;t really want a website; they wanted the thing a website is supposed to do for them — to be found, to bring in customers, to actually work as a channel. A beautiful site nobody can find is a failure that looks like a success.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Combining the build and the growth side into one offering was the task, rather than handing over a site and wishing them luck.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The two halves were put together — custom website development and design on one side, SEO, content strategy and copywriting on the other — so a client got something that was both well‑built and actually discoverable. Those usually get treated as separate jobs, which is how you end up with a gorgeous site that ranks nowhere, or a well‑optimised site that&amp;rsquo;s unpleasant to use. Doing both meant the site was designed from the start to be found, not optimised as an afterthought.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Clients ended up with stronger visibility and more engagement — their sites working as genuine growth channels rather than online brochures. Building the thing and making it findable in one go is what turned a website from a cost into something that actually earned its keep.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built a layered automated test suite — 981 Go tests, 543 frontend and browser specs, 494 SQL behavioural tests — with mutation testing, property‑based tests and an accessibility gate.</title>
    <id>https://software.engineer.company/portfolio/built-a-layered-automated-test-suite-across-four-layers-94/</id>
    <link href="https://software.engineer.company/portfolio/built-a-layered-automated-test-suite-across-four-layers-94/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>Built a layered automated test suite — 981 Go tests, 543 frontend specs and 494 SQL behavioural tests — with mutation, property and accessibility gates.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A platform that keeps its business logic in the database has a testing problem most projects do not. The logic is not in the language the test framework is good at — it is in SQL, behind function boundaries, and SQL is exactly the sort of code that goes untested because testing it is awkward. Add a Go API and a Next.js frontend on top of that, and &amp;ldquo;the important parts are covered&amp;rdquo; quietly turns into &amp;ldquo;the parts that were easy to cover are covered.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Each layer needed its behaviour checked where that behaviour actually lives, rather than everything being checked from the outside through a browser.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Four layers got four kinds of test. The Go API carries 981 test functions across 310 files. The frontend carries 543 Vitest and Playwright specs, including 48 end‑to‑end files and 30 browser specs. The database carries 494 behavioural test files — 116,876 lines of SQL asserting through 6,654 raised exceptions — so a stored function is tested in the database instead of through three layers of application above it. Above those sit the tests that test the tests: Stryker mutation testing deliberately breaks a line and fails when nothing notices, and fast‑check generates inputs nobody thought to write down. An axe‑core gate asserts zero WCAG 2.0 and 2.1 A and AA violations, which makes accessibility a build failure rather than an audit finding months later. Coverage thresholds only ever move upward. The whole suite runs as 10 jobs in a 612‑line workflow.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Changing something structural stopped being frightening, which is the only thing that keeps a codebase this size from calcifying. The honest cost is time — the suite is slow, it taxes every change, and at this size it needs maintenance of its own. What it buys is the ability to keep moving quickly, and that is worth more than the minutes it takes.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the payments and entitlements layer — Stripe alongside Apple and Google in‑app purchase — gating the directory, search and export through an 11‑table access model checked on the server.</title>
    <id>https://software.engineer.company/portfolio/built-the-payments-and-entitlements-layer-96/</id>
    <link href="https://software.engineer.company/portfolio/built-the-payments-and-entitlements-layer-96/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <category term="Security &amp; Access Management" scheme="https://software.engineer.company/services/" />
    <summary>Built the payments and entitlements layer — Stripe with Apple and Google in-app purchase — gating directory, search and export from an access model.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Money is the part of a platform nobody gets to be casual about. A subscription has to survive a card expiring, a refund, a plan change, a webhook that arrives twice and a webhook that arrives out of order. The moment payment can happen on three storefronts — a card on the web, Apple in one app store, Google in the other — there are three different accounts of what somebody bought, and the product still needs one answer to one question: what is this person allowed to do right now?&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Taking money and granting permission had to be two systems rather than one, so that adding a storefront would not mean rewriting every gate in the product.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Stripe handles cards and subscriptions through 18 Go files, and Apple and Google in‑app purchases arrive through their own receipt verification. All three converge on a payments schema of 9 tables and 34 functions — and then stop there. What the product actually asks is a separate access schema, 11 tables and 34 functions, which answers &amp;ldquo;may this account do this?&amp;rdquo; without knowing or caring which storefront paid for it. That answer gates the company directory, search and data export, and it is re‑checked on the server on every request, because a hidden button is a courtesy and not a control.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Adding a storefront now touches the payments side and leaves the gates alone, and a support question about someone&amp;rsquo;s access has one table to look at rather than three. The cost is two schemas where a smaller product would want one, plus an entitlement lookup on requests that would otherwise have been free.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Moved slow work off the request path onto a River job queue — 15 worker modules, 8 scheduled tasks and 20 pg_cron jobs — so a request returns while the work behind it carries on.</title>
    <id>https://software.engineer.company/portfolio/moved-slow-work-onto-a-river-job-queue-97/</id>
    <link href="https://software.engineer.company/portfolio/moved-slow-work-onto-a-river-job-queue-97/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <summary>Moved slow work off the request path onto a River job queue: 15 worker modules, 8 scheduled tasks and 20 pg_cron maintenance jobs.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Some work has no business happening while a user waits. Sending mail, rebuilding a search index, generating a document, recalculating standings — do any of it inside the request and the user watches a spinner for something they never asked to see. Do it in a goroutine instead and it disappears the moment the process restarts, which it will, mid‑deploy, with no record that it was ever supposed to happen.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Background work needed somewhere durable to live: a queue that survives a restart, retries a failure, and can be looked at when something has not happened.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; River was the choice, largely because it keeps its queue in PostgreSQL — the database is already the system of record, so a job and the rows it touches commit or roll back together, and there is no second piece of infrastructure to run and reason about. There are 15 worker modules behind it and 8 scheduled tasks. Underneath, 20 pg_cron jobs handle the maintenance the database is better placed to do itself: pruning partitions, rotating salts, refreshing aggregates. Anything slow enough to notice was moved off the request path and onto one of the two.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Requests return quickly and the slow work still finishes, with retries and a visible history when it does not. Keeping the queue in Postgres rather than a dedicated broker is a deliberate limit: it will not scale forever, and at some volume it becomes the wrong answer. For a platform whose bottleneck is the database anyway, one less moving part was worth more than headroom that was not going to be used.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built first‑party error monitoring and OpenTelemetry tracing rather than buying them — payload sanitising, spike and regression detection, symbolication and a synthetic heartbeat — behind 11 operator views.</title>
    <id>https://software.engineer.company/portfolio/built-first-party-error-monitoring-and-tracing-98/</id>
    <link href="https://software.engineer.company/portfolio/built-first-party-error-monitoring-and-tracing-98/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Monitoring &amp; Observability" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <summary>Built first-party error monitoring and OpenTelemetry tracing — sanitising, spike detection, symbolication and a heartbeat — behind 11 operator views.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The difference between a platform that is up and a platform that is working is whether anyone would know. An error a user hits at eleven at night, on a page nobody tests, is invisible unless something goes and collects it. The usual answer is to buy a hosted error tracker, which is a good answer — and it also means the platform&amp;rsquo;s own errors, stack traces and user context leave for a third party.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Errors and traces had to be collected, grouped and made actionable, without the platform&amp;rsquo;s internals leaving the platform.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Two pieces got built. OpenTelemetry handles tracing over OTLP, so a slow request can be followed across the frontend, the API and the database rather than guessed at. Alongside it sits an in‑house error pipeline — an errmon service and an ingest service — that sanitises payloads before storage, groups errors into recurring problems rather than a flat list, detects spikes and regressions with a cooldown so one bad deploy does not page anyone forty times, symbolicates minified frontend stack traces back into readable code, and runs a synthetic heartbeat to prove that the pipeline itself is alive. It all lands in the database as error events, error groups, an inbox, API latency and stack samples, and it surfaces through 11 operator views, including one for service level objectives.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Errors turn into a queue that someone can work through, and a regression announces itself instead of being discovered by a user. Building rather than buying cost real time and means this is one more thing to maintain — a bought tracker would have been running the same afternoon. What it bought was that nothing sensitive leaves, and that the alerting rules fit this platform rather than a generic one.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built fail‑closed abuse controls — 22 Redis‑backed rate limiters, Cloudflare Turnstile, request idempotency and an origin lock — so the platform sheds bots and floods instead of trusting its callers.</title>
    <id>https://software.engineer.company/portfolio/built-fail-closed-abuse-controls-and-rate-limiting-100/</id>
    <link href="https://software.engineer.company/portfolio/built-fail-closed-abuse-controls-and-rate-limiting-100/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Networking &amp; VPN" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Security &amp; Access Management" scheme="https://software.engineer.company/services/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <summary>Built fail-closed abuse controls: 22 Redis-backed rate limiters, Cloudflare Turnstile, request idempotency and an origin lock on the front door.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A public directory of companies and professionals is a target the day it goes live. Scrapers want the data, spam accounts want the reach, and an endpoint that costs the platform real money to serve — search, export, anything that touches an external API — is worth abusing simply because it is free to call. None of that is malice aimed at this platform in particular; it is background weather on the open internet.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The expensive and abusable paths needed limits that hold under pressure, including the pressure of the limiter&amp;rsquo;s own dependency being unavailable.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; There are 22 rate limiters, each constructed for the path it protects rather than one global cap, because a login attempt, a search and a bulk export are abusive at wildly different rates. State lives in Redis so a limit is shared across instances instead of being per‑process and trivially escaped. The important decision is what happens when Redis is not there: the limiters fail closed. Traffic is refused rather than waved through, which is the less convenient answer and the only defensible one. Around them sit Cloudflare Turnstile on the paths worth challenging, 351 lines of idempotency middleware so a retried write does not become two, an origin lock that refuses requests not arriving through the front door, and a bespoke challenge on the directory itself.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Abuse gets expensive for the abuser and cheap for the platform, and an outage in the limiter&amp;rsquo;s own store degrades into refusal rather than into an open door. Failing closed does mean a Redis problem becomes a user‑visible problem — accepted deliberately, because the alternative is a Redis problem becoming a billing one.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the operator back‑office — around 40 admin routes and 128 components covering claims, moderation, feature flags, cache and diagnostics — so the platform can be run without database access.</title>
    <id>https://software.engineer.company/portfolio/built-the-operator-back-office-for-the-platform-102/</id>
    <link href="https://software.engineer.company/portfolio/built-the-operator-back-office-for-the-platform-102/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="System Administration" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <summary>Built the operator back-office — around 40 admin routes and 128 components for claims, moderation, feature flags, cache and diagnostics.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Every platform quietly grows a second application, and it is usually the one nobody plans. Somebody has to approve a claim, hide a review, turn a feature on for a subset of users, clear a cache, or work out why one account is seeing something odd. When that application does not exist, the answer is an engineer with a database console — which is slow, unlogged, and one typo away from an incident.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Running the platform day to day had to be possible without a shell, so operations belonged to whoever was on duty rather than to whoever had the credentials.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The back‑office came to roughly 40 admin routes built from 128 components, and it covers the work as it actually arrives: adjudicating company ownership claims, moderating reviews and posts, flipping feature flags, inspecting and clearing caches, reading diagnostics, and the reporting the CEO asks for. It runs on the same design system and the same generated API client as the public product, which was the deciding choice — an internal tool built on its own stack becomes the part nobody updates, and then the part nobody trusts.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The people who run the platform can run it, and every action goes through the same authorization and leaves the same trail as anything else. It is a large surface to keep tested and accessible for a small internal audience, and that cost is ongoing. It is still cheaper than the alternative, which is an engineer typing UPDATE against production at nine on a Sunday evening.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built company ownership claims end to end — a user claims a company, an administrator adjudicates, and an approval rewrites the authorization graph that decides who is allowed to edit what.</title>
    <id>https://software.engineer.company/portfolio/built-company-ownership-claims-end-to-end-103/</id>
    <link href="https://software.engineer.company/portfolio/built-company-ownership-claims-end-to-end-103/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <category term="Security &amp; Access Management" scheme="https://software.engineer.company/services/" />
    <summary>Built company ownership claims end to end: a user claims, an administrator adjudicates, and approval rewrites the authorization graph.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A directory seeded from public sources has a structural problem: the companies in it did not put themselves there. Sooner or later somebody from one of them turns up wanting to correct their own entry — and there is no relationship between that person and that record, only an assertion that one exists. Grant it too readily and a competitor edits your page. Grant it too slowly and the directory stays wrong.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; There had to be a route from &amp;ldquo;this is my company&amp;rdquo; to genuine authority over the record, with a human decision in the middle and a trail behind it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Claims were built end to end, across 108 commits and both applications. A user submits a claim with evidence; it enters a queue in the back‑office; an administrator reviews it and approves or rejects it with a reason that goes back to the claimant. The interesting part is what approval does — it is not a flag on a row. Approval rewrites the authorization graph, so the account gains a real relationship to the organization, which is the same relationship every permission check in the platform already consults. The gate is the adjudication, not the code path, and no feature had to learn about claiming in order to respect it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Companies can take over and correct their own entries without anyone editing the database by hand, and every grant of authority has a named approver and a reason attached. Human review is the bottleneck by design; an automated check on a domain name would be faster and would be wrong in exactly the cases that matter most.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the loyalty and reputation system — 67 functions over a 31‑table ledger, with leagues, badges and a redemption shop — taking a row lock on the balance to close the double‑spend window.</title>
    <id>https://software.engineer.company/portfolio/built-the-loyalty-and-reputation-system-105/</id>
    <link href="https://software.engineer.company/portfolio/built-the-loyalty-and-reputation-system-105/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <summary>Built the loyalty and reputation system — 67 functions over a 31-table ledger, leagues, badges and a shop — with row locking on the balance.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Professional networking has a cold start problem: the platform is worth using once other people are already using it, and until then there is little reason to come back. The usual lever is a rewards system — points for contributing, standing that reflects reputation — which is easy to describe and treacherous to build, because the moment points can be spent they are money, and every mistake money makes is available to be made here.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Contribution had to be measurable and rewardable, with a balance that could not be spent twice.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The loyalty schema runs to 31 tables and 67 functions, with standing in a further 5 tables and 32 functions, and discovery and personalization schemas alongside them to decide what a given member sees. On top of the ledger sit leagues, badges and a redemption shop where a balance turns into something real. The part that took the care is the oldest bug in the book: check the balance, then spend it, and two requests arriving together both pass the check. Every mutation takes a row lock on the wallet before reading it, so the second request waits for the first to finish rather than racing it. That the wallet is in the same database as everything else is what makes it possible at all — the balance and the thing it bought commit together or not at all.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Contribution is measured and rewarded, and a balance is arithmetic rather than an approximation. Row locking is the slower answer and was chosen anyway: contention on a wallet is a queue, and the alternative is a member spending the same points twice and someone reconciling it by hand afterwards.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the platform&#39;s social layer — posts, feed, groups, mentions, a follower graph and an occasions digest — on the same function‑first data layer as the rest of the product.</title>
    <id>https://software.engineer.company/portfolio/built-the-platform-s-social-layer-106/</id>
    <link href="https://software.engineer.company/portfolio/built-the-platform-s-social-layer-106/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <summary>Built the platform&#39;s social layer — posts, feed, groups, mentions, a follower graph and an occasions digest — on the same function-first data layer.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A directory of companies is a reference work. People check it and leave. What makes a professional platform worth returning to is other people being on it — which means posts, groups and a reason to come back that is not an email telling you to.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; A social layer had to be added without it becoming a second system with its own rules, its own permissions and its own way of storing things.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Posts, a feed, groups, mentions, a follower graph and an occasions digest went in across 188 commits, all of it on the same function‑first data layer as the rest of the platform. That constraint did most of the work: the follower graph is tables and functions like everything else, a mention resolves through the same identity schema the directory uses, and a post inherits the moderation queue already built for reviews. Nothing here needed its own store or its own permission model. The feed is the one place that pushed back, because assembling a personalised timeline efficiently is a genuinely different problem from fetching a row, and it is where the personalization and discovery work earns its place.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The platform has a reason to be opened on a day when nobody needs to look a company up, and it arrived without a parallel stack to maintain. Whether a social layer is the right investment for a maritime directory is a product question rather than an engineering one — it is here because the product asked for it, and it is built the same way as everything around it.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the hiring marketplace and the seafarer career workspace — 151 stored functions across 48 tables — covering vacancies, applications, certificates, rank progression and verified sea time.</title>
    <id>https://software.engineer.company/portfolio/built-the-hiring-marketplace-and-career-workspace-107/</id>
    <link href="https://software.engineer.company/portfolio/built-the-hiring-marketplace-and-career-workspace-107/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="PostgreSQL" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <summary>Built the hiring marketplace and seafarer career workspace — 151 stored functions across 48 tables — matching vacancies to documented qualifications.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The commercial argument for a maritime professional network is hiring: companies need crew and officers, and seafarers need berths. Both halves already existed on the platform in the wrong shape — companies were in the directory, professionals had profiles, and there was nothing connecting a vacancy to the person qualified to fill it. Qualification is the hard part, because in this industry it is certificates, ranks and documented sea time rather than a job title.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Vacancies, applications and verifiable seafarer credentials had to be modelled properly rather than as free text on a profile.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Two domains got built. The hiring side runs to 32 tables and 118 functions covering vacancies, applications, shortlisting and the employer&amp;rsquo;s view of a pipeline. The career workspace carries 16 tables and 33 functions holding certificates, rank progression and sea time, with document upload and a reviewer queue so a credential is checked rather than claimed. Modelling progression as a graph rather than a list is what makes the matching useful — a rank is reachable from another rank given certain certificates and enough recorded time at sea, and that structure is what lets a vacancy be matched against a career rather than against a keyword. The career workspace ships behind a feature flag and has not been fully released; the hiring side is live.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; A vacancy can be matched against documented qualifications instead of a self‑described job title, which is the whole difference between a job board and a hiring tool in this industry. Verification is the bottleneck, deliberately — a reviewer queue does not scale the way an automated check would, and an automated check would certify people who should not be certified.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Ran the whole company on one 512 MB single‑core host — a git forge, a web server serving seven domains, Tor, two alternate‑protocol servers, backups and intrusion banning — by treating 464 MB of usable memory as the binding architectural constraint.</title>
    <id>https://software.engineer.company/portfolio/ran-the-whole-company-on-one-512mb-host-109/</id>
    <link href="https://software.engineer.company/portfolio/ran-the-whole-company-on-one-512mb-host-109/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Linux &amp; Servers" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Platform Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="System Administration" scheme="https://software.engineer.company/categories/" />
    <category term="Cloud Infrastructure &amp; Migration" scheme="https://software.engineer.company/services/" />
    <category term="Infrastructure as Code" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="System Administration" scheme="https://software.engineer.company/services/" />
    <summary>The whole company runs on a machine that costs less per month than a lunch, and the design is better for the discipline rather than merely cheaper.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The company&amp;rsquo;s production host is a single‑core cloud instance with 512 MB of memory and 10 GB of disk, of which about 464 MB is usable. Everything the business runs in public sits on it: the web server terminating TLS for seven domains, the git forge, a Tor onion service, a Gemini server, a Gopher server, encrypted backups and intrusion banning. The usual reaction to that list is to buy a larger box.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The constraint had to be treated as an architectural input rather than a problem to spend money on, because the honest question was not whether a bigger machine would work but whether the design needed one.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Memory became the argument that settled decisions. There is no monitoring agent, no metrics pipeline and no dashboard — reporting is a pull, seven commands that read the host and render Markdown, changing nothing and running only when asked. The supervisor is systemd rather than a second process manager layered on top of it, and the container plane is Quadlet units under the same supervisor rather than a daemon with its own. Web‑panel platforms were ruled out at the design stage for the same reason. When the question came up of whether the host could carry an onion service, the answer came from a day of measured samples rather than an opinion: available memory never fell below about 310 MB of 464, swap sat at 2.6 per cent and the processor was 99.7 per cent idle.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The whole company runs on a machine that costs less per month than a lunch, and the design is better for the discipline rather than merely cheaper. What it cost is headroom for anything careless — mail is deliberately not on this box at all — it is written as an install scaffold awaiting a host of its own, because a mail server needs headroom this machine has already spent, and that is written down rather than discovered later.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Deployed the company&#39;s own git forge on Soft Serve, private by default with no web panel and its SSH port bound to loopback behind a jump host, and made the landing page in front of it a build artefact of the main site rather than a hand‑kept copy.</title>
    <id>https://software.engineer.company/portfolio/deployed-the-companys-own-git-forge-121/</id>
    <link href="https://software.engineer.company/portfolio/deployed-the-companys-own-git-forge-121/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Linux &amp; Servers" scheme="https://software.engineer.company/categories/" />
    <category term="System Administration" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Infrastructure as Code" scheme="https://software.engineer.company/services/" />
    <category term="System Administration" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>The company hosts its own code on its own hardware, and the page in front of it inherits every check the main site passes rather than drifting away from it in…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The company&amp;rsquo;s source code lived on a third‑party hosting service, which is a reasonable place for it and a poor fit for a business whose argument to clients is that it does not hand their data to intermediaries. Running a forge instead means running a forge: authentication, access control, storage, backups, and a public face for it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; A canonical git server had to be stood up on the existing host, with the smallest possible attack surface and no web administration panel, and a landing page in front of it that does not rot.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The forge is a single binary supervised by the operating system, installed from its vendor&amp;rsquo;s package repository, deliberately chosen for being SSH‑first with no administrative web interface — the less interface, the less to defend. It is private by default: anonymous access is refused, keyless access is refused, and every declared repository is marked private rather than relying on obscurity. Its SSH listener binds only to the loopback interface on port 23231 and is reached from outside through a jump host, so the firewall&amp;rsquo;s declared surface does not grow. A protocol multiplexer that would put HTTPS and git SSH on one public port by inspecting the first bytes of a connection is written and ready behind a master switch, and that switch is off: multi‑user git over the public port is not needed yet, and a listener nobody uses is surface. The landing page in front of it was the more interesting problem. It had been a hand‑maintained copy of the main site&amp;rsquo;s design in its own repository, and every single difference ever found between the two turned out to be an accident rather than a decision: a type scale rendering the wordmark about nine per cent too large, a font declaration that collapsed two weights to one on any machine with the family installed, ornaments hidden below a certain width so they were absent on every phone, a weight used with no font shipped for it, and no main landmark or top‑level heading on any page. Five for five, and not one visible in a screenshot. The copy was deleted; the landing page is now built by the main site&amp;rsquo;s own templates and delivered as an artefact.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The company hosts its own code on its own hardware, and the page in front of it inherits every check the main site passes rather than drifting away from it in ways only a measurement can see. Divergence is still available and now has to be written down.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built a multilingual static site in Hugo across 108 template files including 53 partials, publishing every page in four representations from one content tree in three languages, and again under seven focused subdomains built from that same tree.</title>
    <id>https://software.engineer.company/portfolio/built-a-multilingual-static-site-in-hugo-126/</id>
    <link href="https://software.engineer.company/portfolio/built-a-multilingual-static-site-in-hugo-126/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>The site serves three languages and four representations off one tree, has no backend to attack and nothing to bill, and the one piece of JavaScript in it is…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The company needed a public site that works in three languages, proves capability rather than asserting it, costs nothing to run, and does not hand its visitors to anybody. Most of those are ordinary requirements. Together they rule out almost every content management system, because a runtime backend is a thing to secure, to patch, to pay for, and to explain on a privacy page.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The site had to be generated entirely at build time and still behave like a modern one — searchable, installable, syndicated, printable, and readable by a screen reader in every language it ships.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; It is a static site: 108 template files, of which 53 are partials and 18 are shortcodes, three languages, no runtime backend, and one first‑party script granted as a scoped exception for a portfolio filter whose every control is hidden until it runs. Capability is added at build time rather than in the browser, which is a product decision and is written down as one. Every page is published in four representations from one content tree — HTML, a Markdown twin, a Gemini document and a Gopher menu entry — with the home page adding three syndication feeds and an installable‑application manifest on top. The taxonomies are deliberately two rather than one, and the distinction is load‑bearing: a category is a topic tag on the work, a service is something the company sells, and collapsing them would have made the catalogue a list of skills instead of a list of offerings. The same tree is then built seven more times, once per focused subdomain, by pointing the generator at a different content directory rather than by forking anything.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The site serves three languages and four representations off one tree, has no backend to attack and nothing to bill, and the one piece of JavaScript in it is held to a contract a linter enforces. The cost is that everything interactive has to be solved at build time or not at all, which has ruled out several things that would have been easy with a server and is the reason the search index was costed and parked rather than shipped.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Cut the site&#39;s browser‑driven quality gate from 1,636 seconds to 615 by scheduling its checks longest‑first through a worker pool bounded to four lanes, after measuring that alphabetical order cost 320 seconds against 224.</title>
    <id>https://software.engineer.company/portfolio/cut-the-visual-quality-gate-to-ten-minutes-128/</id>
    <link href="https://software.engineer.company/portfolio/cut-the-visual-quality-gate-to-ten-minutes-128/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>The gate went from 1,636 seconds to 615, while the checks got broader rather than thinner — the serial cost went up and the wall clock came down.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Eleven of the site&amp;rsquo;s checks drive a headless browser: layout at every window shape the design draws, type scale, translated‑text expansion, contrast, accessibility rules, forced colours, mascot sizing, motion, print across six paper combinations, console errors and visual regression. Run one at a time they took 1,636 seconds, a little over twenty‑seven minutes. A gate that takes twenty‑seven minutes is a gate that gets skipped, and a skipped check is indistinguishable from a passing one.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The wall clock had to come down far enough that running them was the default rather than a decision, without weakening any of them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The work was measurement first. Each check was timed individually on a twelve‑core machine: layout at 405 seconds, type at 301, translation at 282, contrast at 189, accessibility at 178, and so on down to seventeen. Two findings shaped the answer. Running all of them at once was slower than running four at a time — 265 seconds against 224 — because each check is itself a browser doing parallel work, and oversubscribing the machine costs more than the concurrency wins. And ordering by longest processing time first beat alphabetical order by nearly a third, 224 seconds against 320, which is the classic scheduling result and shows up here because the checks vary by a factor of twenty in cost. So the runner is a bounded worker pool, sized from the core count with a floor of two and a ceiling of four, fed longest‑first. Alongside it the checks were widened rather than narrowed: they now share one viewport table of twenty‑two window shapes, derived from every media query the stylesheet actually contains, which took the layout check alone from 95 seconds to 405.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The gate went from 1,636 seconds to 615, while the checks got broader rather than thinner — the serial cost went up and the wall clock came down. The measurement is the part worth keeping: two reasonable‑sounding choices, running everything at once and running things in the order they were written, were each measurably worse than the alternative.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Replaced 27 rendered font sizes whose nearest neighbours were 0.6% apart with a six‑step Major Third scale, and wrote the linter that fails the twenty‑eighth.</title>
    <id>https://software.engineer.company/portfolio/replaced-27-font-sizes-with-a-six-step-scale-129/</id>
    <link href="https://software.engineer.company/portfolio/replaced-27-font-sizes-with-a-six-step-scale-129/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>Six sizes where there were twenty-seven, with a stated ratio, a stated measure and a check that refuses the next unplanned value.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A measurement of the site&amp;rsquo;s rendered text found twenty‑seven distinct font sizes in use. Several were separated by less than one per cent — five values between 0.8 and 0.85 of the body size were all live at once, which is a difference no reader can perceive and every future editor will add to. Two of the headings were not sized by the stylesheet at all and were falling through to the browser&amp;rsquo;s own defaults, a ratio of 1.33 that matched nothing else on the page.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The sizes had to become a scale with a stated ratio, and something had to prevent the twenty‑eighth.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The scale is a Major Third, ratio 1.25, six named steps from fine print to display, each a token rather than a value. Headings are mapped explicitly onto steps instead of inheriting whatever the browser thinks, which is what fixed the two that had no size of their own. Only the bottom step is floored, so fine print stays legible on a phone without the whole scale being pinned. Logotypes are exempt by name rather than by accident. The linter is the part that makes it hold: it renders the site and fails on the twenty‑eighth distinct size. It has already earned its place twice — it caught a heading arriving at 1.17 of the body size, which is not a step of anything, and named the ratio in the failure message; and it caught inline code at 0.9, which is precisely the kind of value the scale exists to prevent. Alongside the scale went a measure of about seventy‑two characters, replacing an inherited fixed width that produced ninety‑one characters on a review page and a hundred and ten on the contact page.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Six sizes where there were twenty‑seven, with a stated ratio, a stated measure and a check that refuses the next unplanned value. The constraint is real and occasionally inconvenient: a design that wants a size between two steps has to move to a step or argue for changing the scale, and that argument has been had and lost more than once.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Took WCAG 2.2 Level AA across 32 representative pages — one per template per language — in both colour themes, plus two AAA criteria, with a documented conformance record and a check defending each claim.</title>
    <id>https://software.engineer.company/portfolio/took-wcag-2-2-level-aa-across-the-whole-site-130/</id>
    <link href="https://software.engineer.company/portfolio/took-wcag-2-2-level-aa-across-the-whole-site-130/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Conformance is claimed at Level AA in three languages and two themes, with two AAA criteria beyond it, and every claim has a check behind it.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A consultancy that sells engineering judgement and ships an inaccessible website has a credibility problem before it has an accessibility one. The site also has an unusually wide surface for it: three languages, two colour themes, a full print stylesheet, a dark‑first palette, hand‑drawn annotations and an illustrated character — every one of which is a way to fail a criterion in one configuration while passing it in another.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The site had to conform to WCAG 2.2 at Level AA across every page, every language and both themes, and the conformance had to be defended by checks rather than asserted in a document.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; A conformance record documents every criterion in scope, each row naming what satisfies it and what checks it. Two Level AAA criteria are taken beyond the target — visual presentation in full, and focus appearance, which is offered rather than claimed. The automation runs an accessibility rule engine with its best‑practice rules enabled rather than only the conformance ones, which is a thirty‑rule difference and mattered immediately: one of the extra rules failed the moment it was switched on, because the brand mark sat outside every landmark on every inner page. A second, slower check runs thirty‑two representative pages across two colour schemes and two widths, and what it asserts is unusual: focus is proven in pixels rather than in the document, by pressing the tab key for real and comparing screenshots, because identical pixels mean the user cannot tell where focus is regardless of what the markup says. It also walks the whole page looking for a keyboard trap, applies the specified text‑spacing overrides, doubles the root font size, and measures line length, justification, centring and paragraph spacing.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Conformance is claimed at Level AA in three languages and two themes, with two AAA criteria beyond it, and every claim has a check behind it. What the site says about that on its own credits page is the honest part: no automated tool finds more than about a third of WCAG failures, and there has been no testing with real assistive technology — that limit is written where a reader will see it rather than left out of the claim.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Found and fixed 11 accessibility defects the browser reported as healthy — eight footer links still in tab order behind pointer‑events, a scroll timeline clipping the colophon off three pages, and a rule engine running 70 of its 105 rules.</title>
    <id>https://software.engineer.company/portfolio/fixed-11-accessibility-defects-the-browser-called-healthy-131/</id>
    <link href="https://software.engineer.company/portfolio/fixed-11-accessibility-defects-the-browser-called-healthy-131/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Eleven defects fixed and, more usefully, four rules that outlive them: a guard that tests one axis defends one axis, a rule set that does not contain the rule…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site passed its automated accessibility rules. It also had eleven defects that those rules could not see, because each one was a property of how the page rendered rather than of what the markup said — the kind that a validator reports as healthy and a keyboard user hits within seconds.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The defects had to be found, fixed, and written up as a ledger with the rule each one produced, so the class of defect closes rather than the instance.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; They fell into three groups. Sizing: a stylesheet keyword used as if it were relative pinned a whole region to sixteen pixels while the body ran up to twenty‑two; a subscript element was used to mean &amp;ldquo;smaller&amp;rdquo;; shrinking text lengthened a line until a title ran to ninety‑six characters; and a measured height cap was outgrown by the very text‑spacing override the site claims to support. Focus and clipping: eight footer links stayed in the tab order behind a property that removes pointer interaction and nothing else; an overflow rule created a scroll container nobody wanted; and a scroll‑driven animation, which is simply inactive on a page too short to scroll, permanently clipped the footer off three pages. And two defects in the guards themselves, which are the ones worth naming — every window shape the layout check opened was nine hundred pixels tall, so a landscape phone at 852 by 393 gave seven pixels between two items and no check had ever looked; and the rule engine had been running seventy of its hundred and five rules, so the rule that would have caught the missing landmark was not in the set.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Eleven defects fixed and, more usefully, four rules that outlive them: a guard that tests one axis defends one axis, a rule set that does not contain the rule cannot enforce it, height is a dimension so test it, and prove visibility in pixels rather than in the document. When the layout check was reopened properly it failed a hundred and twenty‑eight times across three languages, including a phone‑sized window where the hero&amp;rsquo;s closing line sat twenty‑one pixels under the fold.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Generated 495 achievement pages across three languages from a read‑only SQLite export, with the page address authored as data so that correcting a sentence no longer moved the page and broke the link.</title>
    <id>https://software.engineer.company/portfolio/generated-321-achievement-pages-from-a-sqlite-export-132/</id>
    <link href="https://software.engineer.company/portfolio/generated-321-achievement-pages-from-a-sqlite-export-132/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Data Pipelines (ETL/ELT)" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="Data Pipeline Development (ETL/ELT)" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Four hundred and ninety-five pages generate from one source, correcting a statement costs nothing, and the addresses this site has ever published continue to…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The company&amp;rsquo;s portfolio content is authored in a separate database — the same one that produces the CV — and the website has to publish it as pages, in three languages, without the two copies drifting apart. The naive approach, writing the pages by hand and keeping them in step, fails on the first correction.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The website had to generate its content from the database as a build input, with page addresses that survive the sentences on them being rewritten.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; An exporter reads the database read‑only and writes one page per achievement per language — 495 pages — plus the taxonomy and services data the templates need. It uses only the standard library, so the site&amp;rsquo;s build has no dependency on the generator&amp;rsquo;s environment, and the committed output means the site builds standalone. The single most consequential decision in it concerns addresses. The site used to derive a page&amp;rsquo;s URL from the leading words of its English statement, so correcting a sentence silently moved the page and broke every link to it — a site whose argument for itself is that it corrects things, charging itself a dead link every time it did. The address is now authored as data: one row per address per achievement, the first is current and every later one is a retired address the site emits as a redirect. Two other traps are recorded from the same work. A period comparison against a text column silently matched all thirty‑two rows because of how the database assigns type affinity across a comparison. And the language list is now the single axis the write loop, the data files and the queries all derive from, so adding a fourth language is one map entry rather than a search.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Four hundred and ninety‑five pages generate from one source, correcting a statement costs nothing, and the addresses this site has ever published continue to answer. The exporter also owns exactly one presentation decision — how services group into themed sections — and that is deliberate: everything else it writes is the database&amp;rsquo;s, and a group heading missing a language fails the export rather than rendering English over translated content.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Closed the colour system at 28 documented colours with a linter that fails a value painted but undocumented, documented but unpainted, mis‑measured, respelled as a literal, or within a perceptual distance of 0.02 of one already there.</title>
    <id>https://software.engineer.company/portfolio/closed-the-colour-system-at-28-colours-133/</id>
    <link href="https://software.engineer.company/portfolio/closed-the-colour-system-at-28-colours-133/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>The palette is a closed, measured set that cannot quietly grow, and the two near-duplicate spellings that prompted it are gone.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Colour on a site with a dark default, a light theme, a print stylesheet, a forced‑colours mode and an illustrated brand does not stay a small set on its own. It had already started to drift in the way it always does: two spellings of the same colour sitting close enough that nobody could tell them apart, values re‑typed as literals beside the tokens that defined them, and documented colours that nothing painted.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The palette had to become a closed set with a stated distinctness threshold, and something had to enforce the closure.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Twenty‑eight colours, each documented with the contrast ratio it measures on the surface it appears on, and eleven brand values declared once as tokens. The threshold is numeric rather than editorial: two colours closer than a perceptual distance of 0.02 in a uniform colour space are one colour with two spellings. The linter fails five different ways — a value painted but undocumented, documented but unpainted, recorded with the wrong ratio, within the threshold of one already there, or a brand value re‑spelled as a literal. It found two immediately: a theme colour that existed in two spellings 0.018 apart, and a background colour standing in for a wall it sat 0.021 from. Transparency uses relative colour syntax rather than the mixing function, specifically because the guard cannot see through a mix and a palette guard that can be evaded is not a guard. The rule that goes with it is short: change a token rather than a rule, add a colour only when none fits, and never lower a documented ratio to make a design work.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The palette is a closed, measured set that cannot quietly grow, and the two near‑duplicate spellings that prompted it are gone. It is a constraint that occasionally says no — a design wanting a slightly different blue has to take the one that exists or make the case for a twenty‑ninth colour, and that case has to include the ratio.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Found that the light theme had been missing a full‑page overlay layer since the day it was written, by asserting that both themes paint every layered surface with the same number of layers.</title>
    <id>https://software.engineer.company/portfolio/found-the-light-theme-missing-an-overlay-layer-134/</id>
    <link href="https://software.engineer.company/portfolio/found-the-light-theme-missing-an-overlay-layer-134/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>A layer missing from one theme now fails a commit, and one that had been missing since it was written is painted.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site ships two colour themes. The dark one is the default and gets looked at constantly; the light one is an override that appears only under a system preference, which means it is seen far less often and by nobody who is checking it. Theme parity is a class of defect where a surface is built once and restated once, and the restatement quietly drops a layer.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Parity needed an assertion, because the only alternative is a person remembering to switch preference and look.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The check is small and structural: for every layered surface, count the layers each theme paints, and fail when the counts differ. It does not compare appearances — themes are supposed to look different — it compares composition, which is the thing that should be identical. It found the defect on its first run. The site&amp;rsquo;s full‑page cover is three layers in the dark theme, a facet pattern over a scrim over the wall, and the light theme restated it with two. The facet overlay had been absent from the light theme from the day it was written, and nothing had ever said so, because a page missing one of three background layers looks like a design decision rather than a bug. The fix was one rule; the write‑up afterwards recorded what the overlay costs in contrast, so the addition is measured rather than assumed to be free.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; A layer missing from one theme now fails a commit, and one that had been missing since it was written is painted. This is the shape of defect the whole checking approach is aimed at — nothing was broken, nothing errored, the page rendered correctly, and it had been wrong for months.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Made reduced‑motion honest after finding eleven selectors still animating under the preference, because a universal transition‑none rule loses on specificity to any rule carrying a class.</title>
    <id>https://software.engineer.company/portfolio/made-reduced-motion-honest-135/</id>
    <link href="https://software.engineer.company/portfolio/made-reduced-motion-honest-135/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>The preference is honoured in fact rather than in intent, and eleven live animations that a blanket rule appeared to have covered are actually covered.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site honoured the reduce‑motion preference with a single rule turning off every transition. It read as complete, it lints clean, and under an emulated reduce‑motion preference eleven selectors were still animating.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The preference had to be honoured in fact, and the reason the obvious rule failed had to become something the next person cannot repeat.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The cause is specificity. A universal rule scores at the bottom of the cascade and loses to any rule carrying a class, so a blanket transition‑none is beaten by every considered animation on the page — which is every animation worth turning off. The fix was structural rather than a patch: anything that moves now lives in one late stylesheet part, and a linter fails a transitioned transform anywhere else. The distinction the rule draws is deliberate and narrower than the obvious one: reduce motion, not colour. A colour fade is not motion and turning it off makes interfaces feel broken to people who did not ask for that, so a transition listing colour properties passes and a transition on movement does not. Motion is also required to be expressed as a transition rather than a keyframe animation, because the former is trivially cancellable and the latter is not. Two related traps came out of the same work and are recorded with measurements: a menu ornament was thirty‑one degrees through a sixty‑degree turn before it was half visible, and because the shape is periodic a half‑turn looks identical for a third of the cost; and five different properties each make an element the containing block for anything positioned relative to the viewport, which had silently shrunk a dismiss layer to a fraction of the screen.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The preference is honoured in fact rather than in intent, and eleven live animations that a blanket rule appeared to have covered are actually covered. The general lesson is the one written at the top of that document: a rule that loses on specificity fails silently, and every report calls it something else.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built a 637‑line print stylesheet against four documented rendering‑engine behaviours, including run‑in headings that printed white on white for any reader whose browser preferred dark.</title>
    <id>https://software.engineer.company/portfolio/built-a-637-line-print-stylesheet-136/</id>
    <link href="https://software.engineer.company/portfolio/built-a-637-line-print-stylesheet-136/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>The site prints on unknown paper in both themes, and the four engine behaviours are written down with the defect each produced.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site&amp;rsquo;s case studies are the artefact somebody prints and takes into a meeting. That makes paper a real output rather than a courtesy, and paper is a different medium from a narrow screen — the reader&amp;rsquo;s paper size is unknown, the reader&amp;rsquo;s orientation is unknown, and the browser&amp;rsquo;s print behaviour differs between engines in ways no screen preview reveals.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The site had to print correctly on unknown paper, in both colour themes, without a separate document being maintained beside the web one.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; It is one 637‑line stylesheet with a single page rule and one print block. The page rule sets a margin and deliberately sets no paper size, so whatever the reader chooses in the dialog comes through and everything else adapts to it. The root font size is pinned in points so every relative measurement has a physical anchor, and one measure of about seventy‑two characters is applied to the four top‑level blocks — which is the sheet winning over the design, since a landscape page would otherwise run a line to about a hundred and ten characters. Four engine behaviours are documented as traps, each having caused a real defect. Viewport units resolve to the page box in one engine and to the window in another, so a page printed from a wide window has a third of every line cropped off in the second. Fixed‑position elements paint on every sheet, so the decorative ones are dropped and two of them are repurposed as a letterhead and a colophon. The palette defaults to white text because the default theme is dark, which printed the four section run‑in labels white on white — the words were simply absent. And paged media cannot split a flex or grid box, which produced a blank first sheet on an achievement page. A six‑part check covers it, testing six paper and orientation combinations, both colour‑scheme preferences, and the real page count against what the content height implies.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The site prints on unknown paper in both themes, and the four engine behaviours are written down with the defect each produced. The known limits are recorded rather than hidden: no page numbers or running heads, one engine ignores widow and orphan control, and drop caps are not universal. A stray comment terminator once made the CSS linter swallow a whole rule and pass clean twice — only the print check noticed.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Mirrored the entire site as 777 Gemini documents and 777 Gopher documents off the same deployed tree, at zero bytes of change to the HTML.</title>
    <id>https://software.engineer.company/portfolio/mirrored-the-site-to-gemini-and-gopher-137/</id>
    <link href="https://software.engineer.company/portfolio/mirrored-the-site-to-gemini-and-gopher-137/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Infrastructure" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>The site is readable over four protocols from one build, at 777 documents each and zero HTML change, and the alternate representations cost about twelve per…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site is static, has no tracking and no runtime backend, and its argument is that a document does not need a megabyte of JavaScript to be read. That argument is easy to make and hard to demonstrate. Two small internet protocols demonstrate it directly, because neither of them can carry a script at all.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The whole site had to be published over Gemini and Gopher from the same content, without a second content tree and without changing the HTML.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Both are output formats of the same build rather than a separate pipeline. The site generator was given custom media types and output formats, and every page, section and taxonomy term gained two more representations alongside its HTML and its Markdown twin. The result is 777 Gemini documents and 777 Gopher menus produced from the same source, deployed to the same tree, and served by two small daemons on the same host. Two properties made it worth doing rather than a curiosity. Both formats are marked as non‑alternative representations, so nothing about the HTML changed — not one byte, and that was measured rather than assumed. And the Gopher format has no way to put a link inside a sentence, since a menu line is tab‑separated fields, so the prose is hard‑wrapped at sixty‑eight columns during the build; that constraint sharpened the writing in a way the HTML never demanded. A Tor onion mirror sits alongside them, and it is the only public face the platform could add without opening a firewall port, because the daemon dials out and nothing dials in.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The site is readable over four protocols from one build, at 777 documents each and zero HTML change, and the alternate representations cost about twelve per cent of the HTML&amp;rsquo;s own weight on disk. Only one of the four is measured for traffic, and that is stated in the platform&amp;rsquo;s own statistics document rather than being quietly ignored.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Replaced 963 anonymous structured‑data blocks that restated the company 1,671 times with one linked graph of 16 types minted from stable origin identifiers.</title>
    <id>https://software.engineer.company/portfolio/replaced-963-structured-data-blocks-with-one-graph-138/</id>
    <link href="https://software.engineer.company/portfolio/replaced-963-structured-data-blocks-with-one-graph-138/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Brand &amp; Marketing" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>One graph with stable identity replaces 963 anonymous restatements, and the pages carry less rather than more.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site emitted structured data the way most sites do: a block per page, each one describing the organisation again from scratch. Across the site that came to 963 blocks restating the same company 1,671 times, anonymously — no stable identifier anywhere, so nothing consuming it could tell that the organisation on one page was the organisation on another.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The structured data had to become one graph with stable identity, rather than a pile of blocks that happen to contain the same words.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Identifiers are now minted from the site&amp;rsquo;s origin — one for the organisation, one for the site, one for the person — and every block references them rather than restating their contents. The identifiers are origin‑wide rather than per language, because the company is the same company in Danish. Sixteen types are in play, covering the service catalogue and its offers, the reviews and the work they describe, the collections and their breadcrumbs. Two decisions kept the weight down. List pages publish their items by URL only rather than inlining them, which was measured: naming them on the portfolio index would have meant 107 full statements and a 52 per cent increase in the compressed page. And the check that guards it does not merely validate syntax — it asserts that every block parses, that every identifier resolves, and that every URL the graph names was actually built, so a graph pointing at a page that does not exist fails the gate.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; One graph with stable identity replaces 963 anonymous restatements, and the pages carry less rather than more. The measurement that started the work is worth keeping in mind: the site had been emitting the same organisation description 1,671 times without anything being able to join them up.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Cut the stylesheet bundle from 87 KB to 31 KB by taking a base64 font out of it, and dropped 756 KB across 22 files that were published on every deploy and referenced by nothing.</title>
    <id>https://software.engineer.company/portfolio/cut-the-stylesheet-bundle-from-87kb-to-31kb-139/</id>
    <link href="https://software.engineer.company/portfolio/cut-the-stylesheet-bundle-from-87kb-to-31kb-139/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>The bundle is 31 KB instead of 87, 756 KB of dead assets stopped being deployed, and each decision has a measurement attached — including the one that went the…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site&amp;rsquo;s stylesheet bundle was 87 KB, which for a document site with no framework is most of a page weight spent before any content arrives. The site also published a directory of icons on every deploy, generated once and referenced by nothing since.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The weight had to come down without the design changing, and it had to come down for a reason that could be pointed at rather than by general tidying.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Measurement first. Two thirds of the largest stylesheet was one base64‑encoded font — about 56 KB of text encoding about 42 KB of font — inlined for a first‑paint benefit that a preloaded external file gets anyway, and paid for on every page whether the weight was needed or not. Taking it out took that file from 84 KB to 28 and the whole bundle from 87 to 31. The three font weights are preloaded instead, and the third joined the list only when field data showed it closing the longest critical chain rather than because three sounded complete. Separately, an audit of what the deploy actually published found twenty‑one generated icons and a duplicate configuration file, 756 KB, shipped every time and referenced from nowhere; and the site&amp;rsquo;s icon file itself came down from 145 KB to 15. A modern image format was evaluated for the brand mark, measured, and declined — it came out larger, and the selection mechanism picks by order rather than by size, so a modern browser would have taken the heavier file everywhere. Two checks now hold the line: no photograph may be displayed wider than half its source pixels, and every illustration must be published at a size its own pixels support at both standard and high density.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The bundle is 31 KB instead of 87, 756 KB of dead assets stopped being deployed, and each decision has a measurement attached — including the one that went the other way, which is the more useful record of the two.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Fixed a sitemap where 172 of 176 URLs shared one modification timestamp, by taking the date from git history after establishing that the export rewrites every file on every run.</title>
    <id>https://software.engineer.company/portfolio/fixed-a-sitemap-with-one-shared-timestamp-140/</id>
    <link href="https://software.engineer.company/portfolio/fixed-a-sitemap-with-one-shared-timestamp-140/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Brand &amp; Marketing" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>A re-sync after a month of content edits now touches eight files of 186 instead of all of them, and the sitemap says something true.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site&amp;rsquo;s sitemap told search engines that 172 of its 176 pages had last changed at the same instant. That is not a subtle inaccuracy — a modification date is a promise to a crawler that a page&amp;rsquo;s content changed, and a site making that promise 172 times simultaneously is either telling the truth about a full rewrite or telling nobody anything useful.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The dates had to describe the content rather than the file, without inventing precision the repository does not have.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The cause was that the date came from the file&amp;rsquo;s modification time and the content export rewrites every file on every run, so a single sync stamped the whole corpus. The fix moves the source to the version history, with a fallback chain that tries an explicit field first, then the commit history, then the file. The trap in that fix is worth recording: the literal field name has to appear in the list or the generator never reads it, so a configuration that looks like it prefers an authored date but omits the name silently ignores every authored date. Two other date decisions came out of the same work. The database now carries when each record was written and last revised, kept deliberately separate from when the work happened — those are years apart and conflating them would date a page written this year to a decade ago. And rows in that file are optional and nothing is derived, because the repository&amp;rsquo;s own history begins after the content did: a missing date leaves the field empty rather than recording a migration, on the principle that a precise wrong number is worse than an absent one, since only the wrong one gets believed.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; A re‑sync after a month of content edits now touches eight files of 186 instead of all of them, and the sitemap says something true. The general rule went into the metadata document alongside it, because the same trap applies to every field that has a fallback chain.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Brought 10,242 lines of quality‑gate JavaScript under a formatter and a linter after establishing it was the largest body of code in the repository and the only one nothing read, fixing 13 findings and suppressing none.</title>
    <id>https://software.engineer.company/portfolio/brought-the-quality-gate-code-under-a-linter-143/</id>
    <link href="https://software.engineer.company/portfolio/brought-the-quality-gate-code-under-a-linter-143/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>The largest and most load-bearing code in the repository is now formatted, linted and type-checked, with thirteen findings fixed and zero suppressions.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The site&amp;rsquo;s quality gate is thirty‑two scripts totalling 10,242 lines of JavaScript. It was, by a wide margin, the largest body of code in the repository, and it was the only body of code nothing read — no formatter, no linter, no type checking. The programs enforcing every rule in the project were the only programs subject to none of them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The checkers had to be held to the standard they exist to enforce, and the formatter had to be fitted to them rather than the other way around.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The formatter came first, and the indentation width was the interesting decision. The repository&amp;rsquo;s default is four spaces; at four spaces the formatter would have rewritten 2,173 lines of the largest checker. An override to two spaces for those files reduced that to 38 lines of genuine drift. The principle recorded with it is that the formatter is fitted to the code, not the code to the formatter — reformatting two thousand lines to satisfy a preference destroys the ability to read the history of the file. Then the linter, which produced thirteen real findings, all fixed and none suppressed. The Python checkers got the same treatment through a type checker configured at a middle strictness with thirty individual strict‑tier rules enabled on top, chosen by measurement: at that setting the tree is silent, and of the thirty candidates twenty‑nine were already silent and one fired — and that one was fixed rather than exempted. The type checker found two real defects the linter had passed clean, both about a value&amp;rsquo;s shape rather than its syntax.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The largest and most load‑bearing code in the repository is now formatted, linted and type‑checked, with thirteen findings fixed and zero suppressions. The reason it matters more than the line count suggests is stated in the plan: a defect in the site&amp;rsquo;s stylesheet shows up as a page that looks wrong, and a defect in a checker shows up as a check that passes when it should not.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built a database‑driven CV, references, portfolio and cover‑letter generator in Python — 41 modules, 10,580 lines — rendering six output formats from one 23‑table SQLite source assembled by a 19‑step idempotent pipeline.</title>
    <id>https://software.engineer.company/portfolio/built-a-database-driven-cv-and-portfolio-generator-144/</id>
    <link href="https://software.engineer.company/portfolio/built-a-database-driven-cv-and-portfolio-generator-144/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="SQL" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Database Design &amp; Modeling" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <summary>Six formats, three languages and five themes come out of one database, and a corrected sentence is corrected once.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A CV, a reference sheet, a portfolio and a cover letter are the same facts arranged four ways, and keeping them as four documents means every correction is made four times and eventually is not. Three languages multiplies that by three. The failure mode is not that a document is wrong; it is that two documents disagree and nothing says which one is current.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; One source of facts had to produce every document, in every language, in every format, with the arrangement decided by code rather than by whoever last edited a file.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The source is a SQLite database of twenty‑three tables — achievements, titles, companies, regions, categories, services, profiles, references, summaries — assembled by a nineteen‑step pipeline that runs from an empty file to a complete database in one command. The steps are ordered and each is written to be safe to repeat, so the pipeline can be run against an existing database without duplicating a row. Forty‑one Python modules totalling 10,580 lines render six output formats from it: HTML, PDF in two variants, plain text, Markdown and JSON, through six Jinja templates and eight stylesheets. Runtime dependencies were held to exactly two, a template engine and a PDF renderer, on the argument that a document generator which cannot be installed in five years has not preserved anything. Targeting is a first‑class concept rather than a manual edit: a profile selects which achievements appear and in what order, so a document aimed at one kind of reader is a query rather than a copy.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Six formats, three languages and five themes come out of one database, and a corrected sentence is corrected once. The cost is that the system is now the only way to produce a document — there is no longer a file to open and edit in a hurry, and adding a language means adding it everywhere before anything builds at all.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Held the generator to 981 test cases at a 92% branch‑coverage floor with warnings treated as failures, and asserted idempotence by running the whole build pipeline twice from an empty file and requiring the second pass to change nothing.</title>
    <id>https://software.engineer.company/portfolio/held-the-generator-to-964-tests-and-a-coverage-floor-145/</id>
    <link href="https://software.engineer.company/portfolio/held-the-generator-to-964-tests-and-a-coverage-floor-145/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Databases" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>The pipeline can be re-run against a live database without fear, which is what makes incremental content work possible at all.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A generator that assembles a database from scratch has a particular kind of bug: it works the first time and corrupts the second. Steps that insert without checking, steps that depend on the order of a previous step&amp;rsquo;s output, steps that are safe alone and not together. None of that shows up in a test that starts from nothing and runs once.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The build had to be proved repeatable rather than merely working, and the test suite had to be large enough and strict enough that a regression could not pass through it quietly.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The idempotence test is the blunt one and the most useful: build the entire database from an empty file, snapshot it, run the whole nineteen‑step pipeline again over the result, and require the second pass to change nothing. Row counts, contents and identifiers all have to match. Around it sit 383 test functions &amp;ndash; 981 cases once the parameterised ones expand &amp;ndash; across forty‑five files and 7,944 lines, covering the pipeline, the renderers, the content loaders, the targeting logic and the checkers. Branch coverage carries a floor of ninety‑two percent enforced in the build rather than reported in a summary, and the suite currently measures about 95 percent, so the floor has headroom without being decorative. Warnings are configured as failures, which is the setting that matters most in practice — a deprecation notice that prints for two years is a deprecation notice nobody reads, and the run that turns it into a red test is the run that gets it fixed.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The pipeline can be re‑run against a live database without fear, which is what makes incremental content work possible at all. The floor is a floor, not a target, and it is worth saying that ninety‑two percent branch coverage still leaves branches nothing has ever taken — the number bounds the risk, it does not remove it, and two of the defects found later in the project were in code the coverage report showed as covered.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Established PDF/UA‑1 conformance across nine documents at 106 of 106 rules, and found the archival variant failing one rule of 146 — a near‑miss that reads as a pass to anyone not running the validator.</title>
    <id>https://software.engineer.company/portfolio/established-pdf-ua-1-conformance-across-nine-documents-146/</id>
    <link href="https://software.engineer.company/portfolio/established-pdf-ua-1-conformance-across-nine-documents-146/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Technical Documentation" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Accessibility conformance is proven at full marks and the archival claim is stated honestly as a near-miss with the specific gap named.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A PDF that looks correct on screen tells you nothing about whether a screen reader can read it, whether its headings form a structure, whether its language is declared, or whether it will still open in twenty years. Accessible‑PDF and archival‑PDF conformance are both machine‑checkable standards, and a document that has never been run through the validator is a document making an unverified claim.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The generated documents had to be measured against both standards, with the result recorded as a number rather than as an intention.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Nine documents — the CV in its variants, the reference sheet, the portfolio and the cover letter, across languages — were run through an independent validator for the accessibility profile and the archival profile separately. The accessibility profile passes at 106 of 106 rules, which required tagged structure, declared document language, alternative text on every non‑decorative graphic, a real title in the metadata, and an explicit reading order rather than the one the layout happens to produce. The archival profile is the more interesting result: it fails exactly one rule of 146. That is the shape of result that is easy to misreport. One hundred and forty‑five passes reads like conformance in a summary, and it is not conformance; it is a document that will be rejected by a system enforcing the standard. The failing rule is recorded with what it is and why it has not been closed, rather than rounded away.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Accessibility conformance is proven at full marks and the archival claim is stated honestly as a near‑miss with the specific gap named. The limit worth being direct about is that both numbers come from one validator; another implementation may disagree, and a rule that passes is only evidence that this checker had nothing to say about it.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Selected every rule the Python linter has as an error, working through 1,815 findings to reach zero, with each of the few exemptions carrying a written reason and two of them backed by a checker instead of a comment.</title>
    <id>https://software.engineer.company/portfolio/enabled-every-python-linter-rule-as-an-error-147/</id>
    <link href="https://software.engineer.company/portfolio/enabled-every-python-linter-rule-as-an-error-147/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="DevOps" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>The linter runs at full strength with zero findings, and every deviation is documented at the line where it is taken.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Most projects pick a comfortable subset of their linter&amp;rsquo;s rules, and the subset is chosen by whichever rules were quiet on the day it was configured. That makes the configuration a record of the code&amp;rsquo;s existing habits rather than a standard the code is held to, and every rule left off is a class of defect nobody will ever be told about.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The default had to be inverted — every rule the tool implements enabled as an error — and the resulting backlog worked to zero rather than negotiated down by turning rules back off.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Selecting the complete rule set produced 1,815 findings on first run. They were worked through by category rather than by file, because the categories tell you something: unused arguments and shadowed builtins are noise, but the security category, the mutable‑default category and the exception‑handling category each pointed at real behaviour. Genuine incompatibilities exist — a formatter and a linter can disagree about the same line, and a few rules contradict the project&amp;rsquo;s own deliberate choices — and each of the small number of exemptions carries a written reason at the point of exemption saying what the rule wanted and why this code does otherwise. Two of them go further and are backed by a check rather than a comment, so the exemption cannot quietly widen: the rule is off, and a test asserts the specific property the rule would have enforced. Type checking runs in strict mode alongside it, which is a separate and harder standard, and it is the one that caught defects the linter could not see because they are about what a value is rather than how it is written.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The linter runs at full strength with zero findings, and every deviation is documented at the line where it is taken. The cost is real and worth naming: the strictest setting produces findings that are genuinely not worth acting on, and someone has to make that judgement 1,815 times rather than once.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Vendored a QR encoder — Reed‑Solomon over GF(256), fixed module layout, eight mask patterns — in 522 lines rather than take a third runtime dependency, and verified it by reading the finished matrix back with an independently written decoder.</title>
    <id>https://software.engineer.company/portfolio/vendored-a-qr-encoder-in-522-lines-148/</id>
    <link href="https://software.engineer.company/portfolio/vendored-a-qr-encoder-in-522-lines-148/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <summary>The runtime dependency count stayed at two, and the encoder is the only vendored component in the project.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The documents needed a QR code linking to the online version. Every available library does this, and taking one would have added a third runtime dependency to a project that had deliberately held itself to two — a template engine and a PDF renderer — on the argument that a document generator should still install years from now.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Either accept the dependency or implement the format, and the implementation had to be verified as correct rather than merely producing something square and black.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The encoder is 522 lines and implements the parts of the specification the use actually needs: byte‑mode encoding, Reed‑Solomon error correction over the Galois field of 256 elements with the generator polynomial built at the required degree, the fixed module layout with its finder patterns, timing patterns and alignment patterns, format and version information, and all eight data mask patterns evaluated against the specification&amp;rsquo;s four penalty rules so the lowest‑scoring mask is chosen rather than a fixed one. The verification is the part that made it defensible. Testing an encoder against its own logic proves nothing, so the finished module matrix is read back by a decoder written independently from the specification&amp;rsquo;s reading order, and the test asserts that the decoded string equals the input. That turns &amp;ldquo;it produces a plausible image&amp;rdquo; into &amp;ldquo;it produces a code that decodes to the right URL&amp;rdquo;, and it is checked on every run rather than once by eye with a phone.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The runtime dependency count stayed at two, and the encoder is the only vendored component in the project. It should be said plainly that this is not a general‑purpose implementation — it supports the modes and versions the documents use and nothing else, and the honest justification is the dependency budget rather than any claim that the result is better than a mature library.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Moved every user‑facing string out of Python into a content tree of 874 files across three languages, after finding dead translations nobody could see were dead and a check silently grading a third of the achievements.</title>
    <id>https://software.engineer.company/portfolio/moved-every-user-facing-string-into-a-content-tree-152/</id>
    <link href="https://software.engineer.company/portfolio/moved-every-user-facing-string-into-a-content-tree-152/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Migrations &amp; Modernization" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Database Migration &amp; Modernization" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <summary>The prose is now editable without touching code and comparable across languages by counting. The cost is that adding a language is now unambiguous rather than…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Every user‑facing string in the generator — every achievement, every review, every section heading, every summary, in three languages — lived inside Python source. That makes editing a sentence a code change, makes reviewing a translation a diff against source, and makes it impossible to tell at a glance whether a language is complete. It also hides the failure mode that matters: a translation can be present, wrong, and unreachable at the same time.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The strings had to move out of code into a content tree that can be counted, compared across languages, and checked without running the renderer.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The result is 874 files under a content directory, organised by kind and then by language: achievements, reviews, summaries, titles, companies, regions, categories, services, profiles, references and cover‑letter fragments. Tab‑separated files where the unit is a line with an identifier, individual files where the unit is a paragraph. The loader reads them at build time and the database is assembled from them, so the tree is the source and the database is derived. Two findings came directly out of being able to count. Some translated strings no longer had any English counterpart — dead entries that nothing rendered and nobody could have noticed while they were embedded in code, because an unreferenced dictionary key looks exactly like a referenced one. And a content check that was supposed to grade every achievement was reading only the subset it could resolve, grading thirty‑seven of ninety‑three and reporting success. Making the corpus a directory made both of these visible as a mismatch in file counts.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The prose is now editable without touching code and comparable across languages by counting. The cost is that adding a language is now unambiguous rather than gradual — the tree makes an incomplete language obvious, which is the point, but it also means partial translations cannot be shipped quietly while they are being finished.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built a cross‑language content check that fails when a translation drops a figure the English states, and when a language uses notation it does not use — finding two Danish descriptions missing a metric and sixteen Ukrainian spans quoting in the English style.</title>
    <id>https://software.engineer.company/portfolio/built-a-cross-language-figure-and-notation-check-153/</id>
    <link href="https://software.engineer.company/portfolio/built-a-cross-language-figure-and-notation-check-153/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Internationalization" scheme="https://software.engineer.company/categories/" />
    <category term="Python" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Data Governance &amp; Quality" scheme="https://software.engineer.company/services/" />
    <category term="Internationalization &amp; Localization" scheme="https://software.engineer.company/services/" />
    <category term="Technical Documentation" scheme="https://software.engineer.company/services/" />
    <summary>Eighteen real defects closed and the class closed with them, since every future translation is compared to its English source before it can be committed.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The most damaging kind of translation error in a CV is not an awkward phrase. It is a number that disappears. An English sentence claiming a fifty‑fold improvement, translated into a sentence that says &amp;ldquo;significantly&amp;rdquo;, is a claim quietly withdrawn in one market and kept in another — and no spell check, grammar check or human reading of the target language alone will ever notice, because the translated sentence is perfectly good prose.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; A check was needed that reads the languages against each other rather than each one on its own, on the two things that must survive translation: the figures, and the notation each language uses to write them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The figure check extracts every number, percentage, multiplier and unit from the English string and requires each one to appear in every translation of that string, with the multiplier forms mapped per language rather than matched literally — the English fifty‑times form corresponds to a specific Danish phrasing and a specific Ukrainian phrasing, and the check knows the mapping instead of demanding the digits alone. The notation check is the mirror of it: each language has conventions it must use and conventions it must not, including decimal separators, thousands grouping and quotation marks. Ukrainian uses low‑nine and high‑six quotation marks; English double quotes in a Ukrainian sentence are as wrong as a missing figure, and far easier to introduce by copying. The first full run found two Danish descriptions where a metric present in English had been dropped, and sixteen Ukrainian spans quoting in the English style.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Eighteen real defects closed and the class closed with them, since every future translation is compared to its English source before it can be committed. What the check cannot do is judge meaning: it proves the number survived and the punctuation is native, and a translation that keeps every figure while getting the claim backwards passes it cleanly.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built a native macOS messaging client in Swift 6 and SwiftUI — 11,141 lines across 53 files — over the C interface of a Rust core linked as a static archive from a pinned revision.</title>
    <id>https://software.engineer.company/portfolio/built-a-native-macos-messaging-client-in-swift-6-154/</id>
    <link href="https://software.engineer.company/portfolio/built-a-native-macos-messaging-client-in-swift-6-154/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Full‑Stack Development" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Full‑Stack Product Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>A native client that starts as a Mac application, uses the platform&#39;s own materials and controls, and carries no embedded browser.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The messaging protocol had a well‑tested core written in Rust and clients on several platforms, but the desktop experience on macOS was a cross‑platform shell — it did not look like a Mac application, did not behave like one, and carried a runtime that a native application does not need. The core exposes its capability through a C interface, which means any language that can call C can build on it, and Swift can.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; A native client had to be built directly on that C interface, in the platform&amp;rsquo;s current language and current interface framework, with the core linked in rather than shipped alongside.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The application is 11,141 lines of Swift across fifty‑three files in the app target, built on SwiftUI with the core linked as a static archive compiled from a pinned upstream revision. Pinning the revision rather than tracking a branch is the decision that makes the build reproducible: the C interface is the contract, and a moving core changes the contract without changing a line of Swift. The layering keeps the unsafe surface small — a thin wrapper owns every pointer and every string that crosses the boundary, models above it are ordinary Swift values, and the interface layer never sees a raw pointer. Everything the C interface returns has an ownership rule, and getting one wrong produces either a leak or a crash with no compiler warning in between, so the wrapper is where the entire project&amp;rsquo;s memory discipline lives. The build is driven by a task runner with fifty‑six targets covering the core compile, the Swift build, the test suites, the linters and the packaging.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; A native client that starts as a Mac application, uses the platform&amp;rsquo;s own materials and controls, and carries no embedded browser. What has not been done should be stated: this is not a shipped release. There is no notarised distribution, no update channel, and one language in the interface, so it is a working application rather than a product anyone else is running.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Stopped an application filling memory at 41 MB a second — a recorded 111 GB of compressed pages on a 36 GB machine — by bounding every event stream, subscribing by event type and putting a rate budget on logging, taking 610,996 log lines down to 1,411.</title>
    <id>https://software.engineer.company/portfolio/stopped-an-application-filling-memory-at-41mb-a-second-155/</id>
    <link href="https://software.engineer.company/portfolio/stopped-an-application-filling-memory-at-41mb-a-second-155/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Monitoring &amp; Observability" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Site Reliability &amp; Monitoring" scheme="https://software.engineer.company/services/" />
    <summary>Memory stays flat under sustained load and the same session that produced 610,996 log lines produces 1,411.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The application filled memory until the operating system killed it. The recorded incident reached 111 GB of compressed pages on a 36 GB machine, climbing at roughly 41 MB a second, and the log file for a single short session held 610,996 lines. A machine in that state is not slow, it is unusable — the kill arrives after the swap has already made everything else on the desktop stop responding.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The growth had to be found rather than guessed at, and every unbounded path had to be given a limit, because one bounded queue next to three unbounded ones is not a fix.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; There were three multiplying causes and the multiplication is why it was so fast. The event stream from the core was consumed without any bound, so events arrived faster than the interface could apply them and the backlog was retained rather than dropped. Every subscriber received every event and filtered afterwards, so the cost of one event was multiplied by the number of listeners, and each listener&amp;rsquo;s filtering allocated. And logging was unbudgeted, so each event produced log lines — which is the compounding term, because the volume of logging was proportional to the volume of the thing going wrong. The fix addressed all three: bounded buffers with an explicit policy for what happens when they fill, subscription by event type so a listener is only woken for events it wants, and a rate budget on logging that collapses repeats rather than writing each one. A regression test drives a high event rate and asserts the memory ceiling holds, so the bound is a property of the build rather than a comment.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Memory stays flat under sustained load and the same session that produced 610,996 log lines produces 1,411. The honest note is that the rate budget on logging discards information: when something goes wrong quickly now, the record of it is deliberately incomplete, and that is a trade made knowingly against the alternative of a machine that stops.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Adopted Swift 6 complete strict concurrency with no actors, bridging a blocking C event loop to the main actor through one producer, one consumer and one ordering — after establishing that a task per event loses the ordering the interface depends on.</title>
    <id>https://software.engineer.company/portfolio/adopted-swift-6-strict-concurrency-over-a-c-event-loop-156/</id>
    <link href="https://software.engineer.company/portfolio/adopted-swift-6-strict-concurrency-over-a-c-event-loop-156/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Performance Tuning" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Solution Architecture" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="Platform &amp; Solution Architecture" scheme="https://software.engineer.company/services/" />
    <category term="Technical Leadership &amp; Consulting" scheme="https://software.engineer.company/services/" />
    <summary>The application compiles under complete strict concurrency with no suppressions, and event ordering is a structural property rather than a hope.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Swift 6&amp;rsquo;s complete strict concurrency checking turns data races into compile errors instead of intermittent crashes. Adopting it against a C library is where it gets difficult: the core&amp;rsquo;s event loop is a blocking call that must run off the main thread forever, and the values it hands back are pointers with no concurrency guarantees at all. The compiler cannot reason about any of it and will refuse everything until the boundary is described explicitly.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Complete checking had to be enabled with no escape hatches, which meant designing the crossing from a blocking C loop to the main actor rather than annotating around it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The obvious approach is an actor per subsystem, and it was rejected on measurement rather than taste. Spawning a task per incoming event lets the runtime schedule them in any order, and the core&amp;rsquo;s event stream is ordered — a message‑changed event that overtakes the message‑created event it refers to produces an interface showing an edit to something that does not exist yet. Actor reentrancy makes this worse, not better, because an actor can suspend mid‑method and process another call. What replaced it is deliberately plain: one producer thread owning the blocking loop, one consumer, one queue between them, and a single hop onto the main actor at the end. Ordering is preserved because there is exactly one path and nothing overtakes anything. The unsafe types crossing that boundary are wrapped in types whose thread‑safety is asserted at the wrapper rather than assumed, and the assertion is documented with why it holds — the pointer is owned by one thread and copied before it is handed over.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The application compiles under complete strict concurrency with no suppressions, and event ordering is a structural property rather than a hope. The cost is that the design is less parallel than it could be: everything funnels through one consumer, and if that consumer ever becomes a bottleneck the fix will require re‑deriving which events can be reordered safely, which is exactly the analysis this avoided.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Wrote a parser that reads the real 7,308‑line C header and verifies every call site, every enum constant and that every pointer‑owning class is final, after a hand‑written placeholder header let calls to three removed functions compile, link and crash.</title>
    <id>https://software.engineer.company/portfolio/wrote-a-parser-that-verifies-every-ffi-call-site-157/</id>
    <link href="https://software.engineer.company/portfolio/wrote-a-parser-that-verifies-every-ffi-call-site-157/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Technical Documentation" scheme="https://software.engineer.company/services/" />
    <summary>The class of defect that produced the original crash cannot recur, because a stale reference is now a build failure rather than a runtime one.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; Early in the project the C interface was represented by a hand‑written header describing the functions the application expected. That header compiled, the application linked, and calls to three functions that no longer existed in the core reached the point of being called and crashed. The compiler and the linker had both been satisfied by a description of the library rather than the library, and the gap only appeared at runtime.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The real header — 7,308 lines and 268 declarations — had to become the authority, and every use of it in the Swift code had to be verified against it automatically rather than by review.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The check is a parser that reads the actual upstream header and builds the set of functions, enum constants and types it declares, then reads the Swift source and resolves every call site and every constant reference against that set. A call to a function the header does not declare fails the build. A reference to an enum constant that has been renamed fails the build. The current count is 132 of the 268 declarations referenced, and knowing which 136 are unused is itself useful, because it says exactly how much of the core the client has not reached. The parser also enforces a rule the compiler cannot: every Swift class that owns a pointer into the core must be final. A non‑final pointer‑owning class can be subclassed, and a subclass that overrides deinitialisation or adds its own lifetime changes when the pointer is freed — a use‑after‑free with no unsafe keyword anywhere near it. The rule is checked by name across the whole tree.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The class of defect that produced the original crash cannot recur, because a stale reference is now a build failure rather than a runtime one. The limitation is that the parser understands the header&amp;rsquo;s declarations and not its semantics: it proves a function exists with a matching name, and a function whose meaning or ownership rule changed upstream while keeping its signature passes without comment.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Named every icon‑only control in the interface for screen readers after finding the send button announced as &#34;arrow up circle, button&#34;, and wrote the linter that requires the label within eight lines of the icon.</title>
    <id>https://software.engineer.company/portfolio/named-all-seventeen-interface-icons-for-screen-readers-159/</id>
    <link href="https://software.engineer.company/portfolio/named-all-seventeen-interface-icons-for-screen-readers-159/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>All seventeen controls announce their function, and an eighteenth cannot be added without one. The rule&#39;s imprecision is real and stated where it is defined:…</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The interface used system symbols for its controls, and a symbol with no accessibility label is read out by the screen reader as its own internal name. The send button announced as &amp;ldquo;arrow up circle, button&amp;rdquo;. So did every other icon‑only control in the application, in its own way — seventeen of them describing their own artwork instead of their function.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; Every icon‑only control needed a label describing what it does, and the fix had to come with a check, because the next icon added would otherwise reintroduce the defect immediately.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Each of the seventeen was given a label naming the action rather than the shape, and where the control&amp;rsquo;s meaning depends on state the label follows the state instead of being fixed. The check is the part worth describing, because a general &amp;ldquo;is this accessible&amp;rdquo; rule does not exist for this. The linter looks for the construction that creates an icon‑only control and then requires an accessibility label within eight lines of it. Eight lines is a deliberately crude heuristic and it was chosen by measurement: it is long enough to span every legitimate way the codebase writes one of these controls with its modifiers, and short enough that a label attached to a different view further down does not accidentally satisfy it. A precise rule here would need to understand SwiftUI&amp;rsquo;s modifier chains as a tree, and the cheap proximity rule catches the actual mistake — which is not a mislabelled control, it is an unlabelled one.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; All seventeen controls announce their function, and an eighteenth cannot be added without one. The rule&amp;rsquo;s imprecision is real and stated where it is defined: it can be satisfied by a label on a neighbouring view, so it proves a label exists nearby rather than proving the label is correct, and correctness still needs someone to listen to it.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built a 48‑token design system on the platform&#39;s own glass material and wrote the linter that rejects a magic number, a hardcoded font size, or an animation that ignores the reduce‑motion preference.</title>
    <id>https://software.engineer.company/portfolio/built-a-48-token-design-system-160/</id>
    <link href="https://software.engineer.company/portfolio/built-a-48-token-design-system-160/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="Frontend Development" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>Forty-eight tokens describe the whole interface, the appearance follows the system, and none of the three erosions can be committed.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; An interface built by adding views accumulates values: a corner radius here, a fourteen‑point font there, a two‑tenths‑of‑a‑second animation somewhere else. Individually each is reasonable and collectively they are a design that cannot be changed, because there is no such thing as &amp;ldquo;the corner radius&amp;rdquo; to change — there are forty of them, slightly different, spread across fifty files.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The visual vocabulary had to be reduced to a named set, expressed against the platform&amp;rsquo;s own material rather than reinvented, and defended by a check so it stays reduced.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The system is forty‑eight tokens in six groups: spacing, typography, colour, radius, elevation and motion. Colour and material are built on the platform&amp;rsquo;s semantic colours and its glass material rather than fixed values, which is what makes the interface follow the system appearance, the accent colour and the contrast settings without any code that watches for them. Typography maps to the platform&amp;rsquo;s text styles so it scales with the user&amp;rsquo;s size preference instead of pinning a point value. The linter rejects three things: a numeric literal where a spacing or radius token belongs, a hardcoded font size anywhere, and an animation declared without honouring the reduce‑motion preference. The third is the one that would otherwise erode fastest, because an animation is added in a moment of polish and the preference check is the part that gets skipped — so the rule makes the animation itself impossible to write without it, rather than asking anyone to remember.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Forty‑eight tokens describe the whole interface, the appearance follows the system, and none of the three erosions can be committed. The limit is that a token system constrains consistency and not quality — everything now matches, and matching is not the same as being well designed, which is a judgement no linter in this project makes.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Reached the half of the messaging core the application had never used — backup transfer, disappearing messages, message editing and resending, verified invitations, proxies and encryption policy — driving every test against the real library with no mocks.</title>
    <id>https://software.engineer.company/portfolio/reached-the-unused-half-of-the-messaging-core-161/</id>
    <link href="https://software.engineer.company/portfolio/reached-the-unused-half-of-the-messaging-core-161/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="APIs &amp; Integration" scheme="https://software.engineer.company/categories/" />
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Backend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Reliability &amp; Backups" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="Backend &amp; API Development" scheme="https://software.engineer.company/services/" />
    <category term="DevOps &amp; CI/CD Automation" scheme="https://software.engineer.company/services/" />
    <category term="Security &amp; Access Management" scheme="https://software.engineer.company/services/" />
    <summary>The previously unreached half of the core is driven by tests against the real library, and the wrapper carries the highest floor in the project.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; The client used roughly half of what the messaging core offers. The unused half was not obscure — backup transfer between devices, disappearing messages, message editing and resending, verified invitation links, proxy configuration and the encryption policy for a chat. Each is a feature a user would expect and each was an untested region of the C interface, which is the more dangerous fact, because an untested region of a C interface is where the ownership mistakes live.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The unreached capability had to be driven and covered, and the tests had to run against the real library rather than a stand‑in.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The rule adopted was no mocks for the core. A mock of a C interface encodes the developer&amp;rsquo;s belief about what the library does, and every defect worth finding here is a place where that belief is wrong — so a passing mock‑based test is evidence about the mock. Instead the tests create real accounts in temporary directories, drive the real library, and assert on what it actually returns, with each suite cleaning up its own state. That is what made the coverage meaningful: exercising backup transfer meant handling a real transfer&amp;rsquo;s state machine and its failure paths, and exercising verified invitations meant constructing the real link format and having the library parse it. Coverage floors were set per layer rather than as one number, at eighty‑four percent for the core wrapper, seventy‑five for utilities and sixty‑five for models, on the reasoning that the layer touching raw pointers should be held highest and a model that is mostly stored properties should not be padded with tests to hit an average.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The previously unreached half of the core is driven by tests against the real library, and the wrapper carries the highest floor in the project. The cost is speed and determinism: real‑library tests are slower than mocks and they can fail for environmental reasons, which is the price of them being able to fail for real ones.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Rewrote the application&#39;s error messages against a written tone standard after a refused sign‑in blamed the user for mistyping when the provider actually required an app‑specific password, and covered it with a test that names the provider.</title>
    <id>https://software.engineer.company/portfolio/rewrote-the-error-messages-against-a-tone-standard-162/</id>
    <link href="https://software.engineer.company/portfolio/rewrote-the-error-messages-against-a-tone-standard-162/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Documentation" scheme="https://software.engineer.company/categories/" />
    <category term="Frontend Engineering" scheme="https://software.engineer.company/categories/" />
    <category term="Product &amp; Requirements" scheme="https://software.engineer.company/categories/" />
    <category term="Security" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Product Strategy &amp; Requirements" scheme="https://software.engineer.company/services/" />
    <category term="Technical Documentation" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <summary>The error surface follows a stated standard and the case that prompted it is covered by a test that fails on the old text.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A sign‑in against a major mail provider failed, and the application told the user their password was incorrect. It was not incorrect. That provider requires an application‑specific password for third‑party clients and rejects the account password regardless of how carefully it is typed. The message sent the user to retype something that could never work, and the actual instruction — go and generate a different kind of password — appeared nowhere.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The error messages had to be rewritten against a written standard rather than patched one at a time, since this one was the visible instance of a habit running through all of them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; The standard has three requirements: say what happened, never imply the user did something wrong when the cause is elsewhere, and give the next action when one exists. Applied across the error surface, most messages violated at least one — several were the underlying library&amp;rsquo;s error string passed through, which describes a condition to a programmer rather than a situation to a person. The provider case was rewritten to name the provider, state that it requires an app‑specific password for other clients, and say where to create one. The test is what stops it regressing, and it is deliberately specific: it drives a sign‑in failure against that provider and asserts the message contains the provider&amp;rsquo;s name and the phrase describing the required credential type. A test asserting only that some error appeared would pass on the original wrong message, so the assertion is on the content, which is the only part that was ever broken.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The error surface follows a stated standard and the case that prompted it is covered by a test that fails on the old text. What remains unresolved is scale: the standard is enforced by review and by one test on one message, and the other providers with their own particular requirements have no equivalent test, so the class is documented rather than closed.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Built the company&#39;s icon and favicon sets from two hand‑made drawings, with every derived asset regenerated by script and a check that fails when a derived file was committed before its source.</title>
    <id>https://software.engineer.company/portfolio/built-the-visual-identity-from-two-drawings-164/</id>
    <link href="https://software.engineer.company/portfolio/built-the-visual-identity-from-two-drawings-164/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-09-13T01:45:50+02:00</published>
    <category term="Automation &amp; CI/CD" scheme="https://software.engineer.company/categories/" />
    <category term="Brand &amp; Marketing" scheme="https://software.engineer.company/categories/" />
    <category term="Design Systems &amp; UI" scheme="https://software.engineer.company/categories/" />
    <category term="Testing &amp; QA" scheme="https://software.engineer.company/categories/" />
    <category term="UX / UI Design" scheme="https://software.engineer.company/categories/" />
    <category term="Web Development" scheme="https://software.engineer.company/categories/" />
    <category term="Brand, Marketing &amp; SEO" scheme="https://software.engineer.company/services/" />
    <category term="UI/UX Design &amp; Design Systems" scheme="https://software.engineer.company/services/" />
    <category term="Website Development &amp; CMS" scheme="https://software.engineer.company/services/" />
    <summary>Two drawings produce every published icon, regeneration is one command, and a stale asset fails the build.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Situation.&lt;/strong&gt; A small company&amp;rsquo;s visual identity is usually bought, generated, or assembled from stock, and the result is an identity that belongs to nobody. The alternative problem is worse: hand‑made artwork that exists only as exported files, where the source drawing is lost, the exports are edited directly, and within a year the versions in use disagree with each other and no original remains to settle it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Task.&lt;/strong&gt; The identity had to be drawn rather than sourced, and the pipeline from drawing to published asset had to be reproducible, so that every file on the site is derivable rather than kept.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Action.&lt;/strong&gt; Two hand‑made drawings sit at the root of the generated artwork — the company mark and the cog that became the favicon — and every icon the site publishes is produced from them. The mark is a raster drawing; the cog is a vector. From these, scripts produce every derived form: the favicon set in its required sizes, the touch icons, and the launch images at each device size. The generation is a task anyone can run, so the question &amp;ldquo;where did this file come from&amp;rdquo; has an answer that is a command. The staleness check is the piece that makes it hold: it compares the commit date of every derived asset against its source and fails when a derived file was committed first, which catches the exact failure this design exists to prevent — someone edits the source, forgets to regenerate, and the published site keeps showing artwork that no longer matches the original. The rule that derived files are never edited by hand is stated where the sources live.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; Two drawings produce every published icon, regeneration is one command, and a stale asset fails the build. The trade‑off is a hard dependency on the toolchain: the derived files are committed so the site builds anywhere, but changing the identity requires the generation tooling to still work, and a source format that stops being readable takes the whole identity with it.&lt;/p&gt;&#xA;</content>
  </entry>
  <entry>
    <title>Opening a dApp inside Solana mobile wallets</title>
    <id>https://software.engineer.company/notes/solana-mobile-wallet-deeplinks/</id>
    <link href="https://software.engineer.company/notes/solana-mobile-wallet-deeplinks/" rel="alternate" type="text/html" />
    <updated>2026-09-13T01:45:50+02:00</updated>
    <published>2026-08-10T00:00:00Z</published>
    <summary>Why Phantom, Solflare and Backpack browse deep links fail on mobile, and the exact formats, trigger rules and fixes that make them work.</summary>
    <content type="html">&lt;p&gt;A React dApp built on &lt;code&gt;@solana/wallet-adapter-react&lt;/code&gt; connects desktop wallets&#xA;without trouble, but on a phone the same flow falls apart: the wallet has to&#xA;open the dApp inside its own in-app browser, and the deep links that should&#xA;make that happen quietly do not. Backpack lands on a &amp;ldquo;download the app&amp;rdquo; page;&#xA;Solflare opens the app but never the site; every variant seems to fail. We took&#xA;the problem apart, and it turned out to be four separate problems wearing one&#xA;symptom.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-four-problems&#34;&gt;The four problems&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Backpack link was malformed.&lt;/strong&gt; The only documented format is&#xA;&lt;code&gt;https://backpack.app/ul/v1/browse/&amp;lt;url&amp;gt;?ref=&amp;lt;ref&amp;gt;&lt;/code&gt; — a universal link with&#xA;the target URL in the path and a required &lt;code&gt;ref&lt;/code&gt;. A custom-scheme guess like&#xA;&lt;code&gt;backpack://ul/v1/browse?url=...&lt;/code&gt; matches no route the app registers, so the&#xA;user ends on the wallet&amp;rsquo;s install page.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Solflare needs its universal link too:&lt;/strong&gt;&#xA;&lt;code&gt;https://solflare.com/ul/v1/browse/&amp;lt;url&amp;gt;?ref=&amp;lt;ref&amp;gt;&lt;/code&gt;, not the bare&#xA;&lt;code&gt;solflare://&lt;/code&gt; scheme. A bare scheme can launch the app without routing it —&#xA;which is exactly &amp;ldquo;the app opens, but the site tab has to be opened by hand&amp;rdquo;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Both parameters must be encoded.&lt;/strong&gt; &lt;code&gt;url&lt;/code&gt; is the full absolute dApp address&#xA;and &lt;code&gt;ref&lt;/code&gt; is the requesting origin, each passed through &lt;code&gt;encodeURIComponent&lt;/code&gt;.&#xA;An unencoded &lt;code&gt;?&lt;/code&gt; or &lt;code&gt;&amp;amp;&lt;/code&gt; in the target corrupts the parse, and the wallet&#xA;opens on its home screen instead of the browser tab.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The trigger matters as much as the link.&lt;/strong&gt; Universal links only switch apps&#xA;on a navigation the operating system trusts — and they deliberately do&#xA;nothing when pasted into the address bar, which is also how a perfectly&#xA;correct link &amp;ldquo;fails&amp;rdquo; during testing.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;the-documented-formats&#34;&gt;The documented formats&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Phantom: &lt;code&gt;https://phantom.app/ul/browse/&amp;lt;url&amp;gt;?ref=&amp;lt;ref&amp;gt;&lt;/code&gt; — no &lt;code&gt;/v1&lt;/code&gt; in this&#xA;one.&lt;/li&gt;&#xA;&lt;li&gt;Solflare: &lt;code&gt;https://solflare.com/ul/v1/browse/&amp;lt;url&amp;gt;?ref=&amp;lt;ref&amp;gt;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Backpack: &lt;code&gt;https://backpack.app/ul/v1/browse/&amp;lt;url&amp;gt;?ref=&amp;lt;ref&amp;gt;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;One pattern serves all three:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const WALLET_BROWSE = {&#xA;  phantom: (url, ref) =&amp;gt;&#xA;    `https://phantom.app/ul/browse/${url}?ref=${ref}`,&#xA;  solflare: (url, ref) =&amp;gt;&#xA;    `https://solflare.com/ul/v1/browse/${url}?ref=${ref}`,&#xA;  backpack: (url, ref) =&amp;gt;&#xA;    `https://backpack.app/ul/v1/browse/${url}?ref=${ref}`,&#xA;};&#xA;&#xA;function walletBrowseLink(&#xA;  walletName,&#xA;  targetUrl = window.location.href,&#xA;) {&#xA;  const build = WALLET_BROWSE[walletName.toLowerCase()];&#xA;  if (!build) return null;&#xA;  return build(&#xA;    encodeURIComponent(targetUrl),&#xA;    encodeURIComponent(window.location.origin),&#xA;  );&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;triggering-the-link-so-ios-and-android-accept-it&#34;&gt;Triggering the link so iOS and Android accept it&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Render a real anchor, precomputed.&lt;/strong&gt; A plain&#xA;&lt;code&gt;&amp;lt;a href={walletBrowseLink(&#39;phantom&#39;)}&amp;gt;&lt;/code&gt; is the most reliable trigger on both&#xA;platforms.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;If it must be programmatic&lt;/strong&gt;, assign &lt;code&gt;window.location.href&lt;/code&gt; synchronously&#xA;inside the tap handler — no &lt;code&gt;await&lt;/code&gt;, no &lt;code&gt;fetch&lt;/code&gt;, no &lt;code&gt;setTimeout&lt;/code&gt; first. After&#xA;asynchronous work the gesture context is gone, and iOS falls back to the&#xA;wallet&amp;rsquo;s website. Never &lt;code&gt;window.open&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Never test by pasting into the address bar.&lt;/strong&gt; Universal links deliberately&#xA;do not fire there; test with a tapped link or a QR code scanned by the&#xA;camera.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mind the messenger webviews.&lt;/strong&gt; Opened inside Telegram&amp;rsquo;s or Instagram&amp;rsquo;s&#xA;in-app browser, universal links are frequently swallowed and the wallet&amp;rsquo;s&#xA;plain website loads instead. User-agent detection is heuristic at best, so&#xA;also give users a visible escape hatch: &amp;ldquo;open in Safari or Chrome, then&#xA;connect&amp;rdquo;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;the-bigger-fix-on-android&#34;&gt;The bigger fix on Android&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Hand-rolled deep links are the iOS story. On Android, Solana Mobile&amp;rsquo;s Mobile&#xA;Wallet Adapter lets a dApp running in the mobile browser connect straight to&#xA;the installed wallet app, with no in-app-browser detour at all. Recent versions&#xA;of &lt;code&gt;@solana/wallet-adapter-react&lt;/code&gt; register the mobile adapter automatically, so&#xA;upgrading the wallet-adapter packages can fix Android by itself. The target&#xA;architecture: Mobile Wallet Adapter on Android, browse universal links on iOS,&#xA;where Apple allows no equivalent.&lt;/p&gt;&#xA;&lt;h2 id=&#34;verifying-on-a-device&#34;&gt;Verifying on a device&lt;/h2&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Real device, wallet installed, link opened from the system browser — not&#xA;from a messenger.&lt;/li&gt;&#xA;&lt;li&gt;Tap a rendered link or scan a QR code; never paste into the address bar.&lt;/li&gt;&#xA;&lt;li&gt;Confirm the wallet opens and the dApp loads in its in-app browser tab — the&#xA;second half is the part that fails.&lt;/li&gt;&#xA;&lt;li&gt;Repeat without the wallet installed: the universal link should degrade to&#xA;the wallet&amp;rsquo;s website. That page appearing while the app is installed means&#xA;the link or the trigger is still wrong.&lt;/li&gt;&#xA;&lt;li&gt;Then test the messenger path, and add the &amp;ldquo;open in browser&amp;rdquo; hint if it&#xA;fails there.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;sources&#34;&gt;Sources&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.phantom.com/phantom-deeplinks/deeplinks-ios-and-android&#34;&gt;Phantom: deep links on iOS and Android&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.solflare.com/solflare/technical/deeplinks/other-methods/browse&#34;&gt;Solflare: the Browse deep link&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.backpack.app/deeplinks/other-methods/browse&#34;&gt;Backpack: the Browse deep link&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.solanamobile.com/mobile-wallet-adapter/mobile-apps&#34;&gt;Solana Mobile: Mobile Wallet Adapter&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content>
  </entry>
</feed>
