<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.bash-365.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.bash-365.com/" rel="alternate" type="text/html" /><updated>2026-11-02T17:01:28+00:00</updated><id>https://www.bash-365.com/feed.xml</id><title type="html">BASH</title><subtitle>Enterprise-grade IT for small business — cloud, ERP, data, and AI-augmented automation from Denver, Colorado</subtitle><author><name>Amr</name></author><entry><title type="html">Badwill impairment: three years of building without the meeting</title><link href="https://www.bash-365.com/posts/2026/11/02/badwill-impairment-three-years-of-building-without-the-meeting/" rel="alternate" type="text/html" title="Badwill impairment: three years of building without the meeting" /><published>2026-11-02T03:30:00+00:00</published><updated>2026-11-02T03:30:00+00:00</updated><id>https://www.bash-365.com/posts/2026/11/02/badwill-impairment-three-years-of-building-without-the-meeting</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/11/02/badwill-impairment-three-years-of-building-without-the-meeting/"><![CDATA[<p>Three years ago this week, the noise stopped.</p>

<p>If you have worked in a large company, you know the noise. The weekly stand-up that never moves. The transformation roadmap with no roads on it. And the scariest sound in the building: an emergency meeting about the “cash flow imperative,” called by leadership, attended by everyone with an opinion, and closed to the person holding the system design that fixed the cash problem the meeting was about.</p>

<p>That person was me. The meeting went ahead without the design, the way those meetings do. The verdict came down. Then a baby started crying, the real kind, and I went home to the best paternity leave in corporate history, approved by nobody. (<a href="https://bamr87.github.io/posts/2026/11/01/badwill-impairment/">The short version of that story</a> is on my own site.)</p>

<p>This is the ledger of what one person and governed artificial intelligence (AI) built in the three years since. It is kept the way I was trained to keep a ledger: every entry has a source, and the entries that are still plans are booked as plans.</p>

<h2 id="goodwill-badwill-and-the-meeting-i-wasnt-invited-to">Goodwill, badwill and the meeting I wasn’t invited to</h2>

<p>In accounting, goodwill is the premium a buyer pays over the fair value of everything it can count: the reputation, the relationships, the know-how that never fits on a balance sheet. When that premium turns out to be worth less than the price, the company writes it down. The write-down is a goodwill impairment, and it usually arrives with a press release about headwinds.</p>

<p>Badwill is the rarer opposite. It is negative goodwill, booked when a buyer gets a business for less than its assets are worth. Accountants call it a bargain purchase gain, and the standards make you check your math again before you are allowed to believe it.</p>

<p>Here is the corporate version of the trade, which no standard covers. A company decides that the people who understand both the ledger and the systems underneath it are a cost to be impaired, and books the savings as a gain. What walked out the door was know-how nobody had put on the books, sold at a bargain to whoever wanted it next. In my case, the buyer was me. I booked the badwill.</p>

<h2 id="what-got-built-instead">What got built instead</h2>

<p>Six entries. Every one is public, every one has a link, and every one is followed by the note an auditor would want.</p>

<h3 id="fredgar-ai-an-analyst-that-reads-the-filing-before-the-meeting">fredgar-ai: an analyst that reads the filing before the meeting</h3>

<p><a href="https://github.com/bamr87/fredgar-ai">fredgar-ai</a> pulls company data from the U.S. Securities and Exchange Commission’s (SEC) Electronic Data Gathering, Analysis, and Retrieval system (EDGAR), and optional macroeconomic series from Federal Reserve Economic Data (FRED), into a database of its own. It runs on Django with a React front end, uses SQLite until you point it at PostgreSQL, and caches what it pulls from the SEC first, to stay inside the SEC’s fair-access limits. A versioned application programming interface (API) serves the data, and every Monday a workflow publishes <a href="https://bamr87.github.io/fredgar-ai/">a static site of company financials</a> that needs no login.</p>

<p><img src="/assets/images/posts/badwill-impairment/fredgar-ai-company-page.jpg" alt="fredgar-ai's public site showing Microsoft Corporation's company page: a contents list, a summary card with ticker, CIK and filing counts, and a financial snapshot of revenue, income, assets, liabilities, equity and cash" /></p>

<p>It also shows you where the data is strange, which is more than most dashboards will admit. On Microsoft’s page, the latest figure on the Revenue line is dated 2010, while everything beside it is from 2026. Filings tag their numbers in more than one way, and the page shows you the seam instead of hiding it. A system that puts the oddity on screen beats a meeting that discovers it in the fourth quarter.</p>

<p><em>Auditor’s note:</em> the README promises AI-written summaries. What exists today is an optional, administrator-only analysis of leadership language, grounded in filing excerpts and switched off unless someone supplies a model credential. The summaries are on the roadmap, which is where corporations keep them too.</p>

<h3 id="law-ai-a-lawyer-that-keeps-its-prompts-at-home">law-ai: a lawyer that keeps its prompts at home</h3>

<p><a href="https://github.com/bamr87/law-ai">law-ai</a> is a legal research platform: Django and LangGraph agents working over a Neo4j graph of cases and the citations between them. It answers with Claude by default and falls back through xAI’s Grok to Ollama, a model running on the same machine. One setting pins it to the local model, so nothing in a prompt leaves the building. Court opinions from CourtListener, Colorado statutes and the Denver municipal code land in a quarantine queue that a person approves before anything reaches the graph. A LegalBench harness grades its answers every night, and a regression fails the run.</p>

<p><img src="/assets/images/posts/badwill-impairment/law-ai-agent-operations.jpg" alt="law-ai's Agent Operations screen: counts of agents, audit events, open backlog and active specs, the model provider chain from Claude to a local Ollama model with each one's status, and per-agent run metrics" /></p>

<p><em>Auditor’s note:</em> this one is an experiment. Its most quotable feature, a reviewer that cross-examines every pull request for license and compliance risk, is imported but not switched on. The lawyer has passed the bar in continuous integration (CI) and is waiting for its first case.</p>

<h3 id="zer0-cms-the-content-system-that-never-skips-the-approval">zer0-CMS: the content system that never skips the approval</h3>

<p><a href="https://github.com/bamr87/zer0-CMS">zer0-CMS</a> started as a fork of Front Matter CMS, a Visual Studio Code extension, and is no longer one. The extension was rewritten, and the platform was rebuilt on Django and Wagtail with a React studio in front. It runs on SQLite or PostgreSQL, ships as a container, and the Ruby on Rails content management system (CMS) it replaced is retired.</p>

<p>Its organizing idea is the one this practice sells. AI agents are users with permissions, not ghosts in the content. What an agent writes arrives as a suggestion, shown as a diff, and a person accepts or rejects it.</p>

<p><img src="/assets/images/posts/badwill-impairment/zer0-cms-review-inbox.jpg" alt="The zer0 studio's Review screen: an SEO agent's two suggestions for a page, one shown as a red and green text diff, each with Accept and Reject buttons" /></p>

<p>Publishing is off by default and runs through one fixed sequence (draft, brand guard, approval, publish, ledger), whether the target is a site, LinkedIn, X, WordPress or a newsletter. Here it is sending an earlier BASH post to X, with the note that it was written to the ledger that keeps it from going out twice.</p>

<p><img src="/assets/images/posts/badwill-impairment/zer0-cms-published-to-x.jpg" alt="The zer0 studio's post panel for an article shared to X: status Published, the X post's ID and time, and a note that it was written to the site's ledger file" /></p>

<p><em>Auditor’s note:</em> much of the README describes work in progress, and the repository’s history includes the original Front Matter project’s commits, which are not mine to count.</p>

<h3 id="lifehackerdev-a-newsroom-with-one-robot-and-one-editor">lifehacker.dev: a newsroom with one robot and one editor</h3>

<p><a href="https://lifehacker.dev/">lifehacker.dev</a> is a Jekyll site, 317 posts deep, written by Claude Code and published by a human. The robot opens pull requests. Branch protection and a code-owners file make a person approve every one, and a verification job rebuilds the site exactly as GitHub Pages will before it checks the front matter, the brand rules, the links and the drift. The brand is data: identity, voice and glossary in files the robot reads before it writes. Its news desk, The Wire, has a press charter this article has borrowed: satire in the framing, never the facts.</p>

<p><img src="/assets/images/posts/badwill-impairment/lifehacker-dev-home.jpg" alt="The lifehacker.dev home page: the site's navigation, and a top story card for a Field Notes piece called The Sixty-Five Thousand Doors" /></p>

<p><em>Auditor’s note:</em> every automated loop idles until its repository switch is turned on, and as of November 1, 2026, every switch is off. The robot is on leave. Unlike mine, its leave required a configuration change.</p>

<h3 id="csv-grid-editor-the-spreadsheet-that-stays-a-text-file">CSV Grid Editor: the spreadsheet that stays a text file</h3>

<p><a href="https://github.com/bash-365/csv-vscoode">CSV Grid Editor</a> opens comma-separated values (CSV) and tab-separated files in a spreadsheet grid inside Visual Studio Code, on top of the file itself. The text stays the source of truth, so save, undo and Git behave as usual, and nothing reformats your dates or drops your leading zeros on the way back out. It scrolls past 100,000 rows, filters columns the way Excel does, loads the file into an in-memory SQLite table you can query with Structured Query Language (SQL), and saves repeated cleanup as pipelines that can run every time you save.</p>

<p><img src="/assets/images/posts/badwill-impairment/csv-grid-editor.jpg" alt="CSV Grid Editor in Visual Studio Code: a sales CSV open as a grid with filterable columns, a toolbar with SQL, pipeline and statistics, and a sidebar of settings, pipelines and files" /></p>

<p><em>Auditor’s note:</em> it is finished. Version 1.0.0 is on the Visual Studio Marketplace (443 installs as of November 1, 2026) and Open VSX (726 downloads), and the repository is archived. Finished is a status corporate project reports rarely offer. The usual options are on track, at risk and re-baselined.</p>

<h3 id="the-control-plane-forty-four-repositories-and-no-status-meeting">The control plane: forty-four repositories and no status meeting</h3>

<p>The thing I most wanted to build inside a company was a control plane over everything I maintain. <a href="https://github.com/bamr87/bamr87">bamr87/bamr87</a> is my GitHub profile repository, and it is that control plane. A registry lists 44 projects: 38 mounted as submodules, six hosted elsewhere, 21 of them active. The profile’s project list is generated from that registry after every merge. A reconciler runs every morning, fixes renamed repositories and drifted links, and opens a pull request with the fix; a repository that has disappeared is filed as an issue, never deleted on a guess. A drift check runs on every pull request and fails it when the registry, the submodules and the folders disagree, or when someone hand-edits a generated file.</p>

<p>The agents work the way a good team does, minus the stand-up. A daily pipeline scans for problems, writes each one up with its evidence, implements a fix on a draft pull request and marks it ready for review. It never merges. A second agent opens draft fixes for failing workflows, and a third answers when someone mentions it. Releases are cut by release-please, a deterministic tool, because some things should not be creative.</p>

<p>All of it renders into <a href="https://bamr87.github.io/bamr87/">the Harness Console</a>, rebuilt from the repository every day: every project, its health, its AI harnesses and what they cost.</p>

<p><img src="/assets/images/posts/badwill-impairment/harness-console.jpg" alt="The Harness Console's home page: a profile header, tiles counting 44 projects, 20 needing attention, 6 failing workflows, 13 triage flags, 14 days of Claude in CI cost and Actions hours, and cards for portfolio, agentic harness management and GitHub orchestration" /></p>

<p><em>Auditor’s note:</em> the console is honest about the fleet, too. On the day this screenshot was taken it showed 20 projects needing attention and 6 failing workflows. And the parts of the pitch that sounded best (observability for every service, infrastructure reconciled from Git, a phone that watches the fleet) exist today as three repositories marked “foundation (phase 1).” In a corporation, those would be roadmap items. The difference is that these roadmap items have a README and a commit history.</p>

<h2 id="why-one-person-can-do-this-now">Why one person can do this now</h2>

<p>None of this is a story about talent. It is a story about where the controls live.</p>

<p>A corporation manages risk with meetings: the stand-up, the steering committee, the change advisory board, and the emergency session about the cash flow imperative. Each one asks people to remember the rules and agree, again, that they still apply. Everything above manages risk with files and checks instead. The rules live in the repository. Recurring work runs from versioned playbooks. Automation checks every change before it ships and fails loudly when something is wrong. AI agents draft, open pull requests and report what they did, and a named person approves anything that leaves the building, the same way this site posts to LinkedIn (<a href="/posts/2026/10/29/automated-linkedin-posts-approved-by-a-person/" class="wiki-link" data-wiki-target="Automated LinkedIn posts, approved by a person">Automated LinkedIn posts, approved by a person</a>). Deterministic tools carry the load, and the model is spent only where judgment is needed.</p>

<p>That is the whole trick, and it is the same one we use with clients. A small business does not need a large information technology (IT) department to run enterprise-grade systems. It needs the department’s controls, written down, checked by machines and approved by a person who knows what the numbers mean.</p>

<h2 id="what-your-business-can-borrow">What your business can borrow</h2>

<ul>
  <li><strong>Turn one meeting into a check.</strong> Pick the recurring meeting whose real job is to catch a mistake, and write the mistake down as a test, a lint rule or a checklist a script can run. Keep the meeting until the check has caught something twice.</li>
  <li><strong>Put the approval rules in a file.</strong> Who may publish, merge, pay or delete belongs in one file under version control, read by people and machines alike.</li>
  <li><strong>Let agents propose, and keep the merge for a person.</strong> Give AI the pull request and a person the merge button. The audit trail writes itself.</li>
  <li><strong>Keep a ledger of what went out.</strong> Posts, payments, emails, deployments: if it can happen twice by accident, record it once on purpose.</li>
  <li><strong>Count the know-how before you impair it.</strong> Before the next restructuring, list the people who understand both the ledger and the system underneath it. That list is the goodwill you are about to write down, and somebody else will book it as a bargain.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If your business is holding the cash flow imperative meeting right now, bring the system design instead. We build enterprise-grade software and automation for small and mid-sized businesses, with the controls written down and a person approving every change: see <a href="/services/dev/">software development</a>.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="muses" /><category term="badwill-impairment" /><category term="governed-ai" /><category term="agentic-ai" /><category term="open-source" /><category term="enterprise-software" /><category term="continuous-integration" /><category term="small-business" /><summary type="html"><![CDATA[Three years after the noise stopped, one person and governed AI built six enterprise-grade systems. Here they are, with screenshots and what to borrow]]></summary></entry><entry><title type="html">Automated LinkedIn posts, approved by a person</title><link href="https://www.bash-365.com/posts/2026/10/29/automated-linkedin-posts-approved-by-a-person/" rel="alternate" type="text/html" title="Automated LinkedIn posts, approved by a person" /><published>2026-10-29T12:00:00+00:00</published><updated>2026-10-29T12:00:00+00:00</updated><id>https://www.bash-365.com/posts/2026/10/29/automated-linkedin-posts-approved-by-a-person</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/10/29/automated-linkedin-posts-approved-by-a-person/"><![CDATA[<p>Most small businesses have more worth posting than they ever post. The write-up of last quarter’s migration, the how-to a customer asked for twice, the lesson learned the hard way: it sits on the website and never reaches the company’s LinkedIn page, because posting is a separate chore that starts from a blank box. The tools that take the chore away usually do it with a scheduler and a bulk queue, which is the opposite of what an owner wants from the account that speaks for the business.</p>

<p>Here is the pipeline we run for the BASH Consulting company page, and what we found this week when we checked whether it still worked. An artificial intelligence (AI) agent writes the first draft, a named person approves every post, and nothing reaches LinkedIn that a person did not sign off on. It runs on plain files in a Git repository, GitHub Actions for continuous integration (CI), and LinkedIn’s Posts application programming interface (API).</p>

<h2 id="the-rule-nothing-posts-without-a-persons-approval">The rule: nothing posts without a person’s approval</h2>

<p>A company page is the business talking. A post that goes out unread, with a wrong claim, an invented number, or a tone you would never use, can be deleted, but not before people have seen it. So the design starts from one rule and works backward: a person approves every post, and the approval is a record rather than a habit.</p>

<p>Everything else can be automated because that rule holds. It is the same split we describe in <a href="/ai-operations/">how we run the practice</a>: rules live in files, agents draft, and people approve anything customer-facing.</p>

<h2 id="how-a-post-moves-from-the-website-to-linkedin">How a post moves from the website to LinkedIn</h2>

<ol>
  <li><strong>A page goes live.</strong> The pipeline lists every published post that has a title, a description, and a public address, and hasn’t been shared yet.</li>
  <li><strong>Someone asks for a draft.</strong> A person, or an agent working for one, runs the draft command. It writes one Markdown file in <code class="language-plaintext highlighter-rouge">drafts/linkedin/</code>, marked <code class="language-plaintext highlighter-rouge">pending</code>, with a plain first draft taken from the page. An agent then rewrites it following a written procedure: the hook lands in about the first 140 characters, before LinkedIn’s “see more” fold. It has exactly one call to action, two or three hashtags, no banned phrases, and nothing invented.</li>
  <li><strong>A preview shows the exact request.</strong> Before anything is sent, the preview prints the payload LinkedIn would receive, where the fold falls, the card image it would upload, the brand guard’s findings, and every gate still closed. It makes no network call. LinkedIn does not scrape the link for this kind of post: the card’s title, description, and thumbnail travel in the request itself, per the <a href="https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api">Posts API documentation</a>. So what the preview shows is what LinkedIn shows.</li>
  <li><strong>A person merges the draft.</strong> The draft goes up as a pull request. The reviewer edits the copy in place and merges it. The merge is the approval, and the repository’s history records who approved what, and when.</li>
  <li><strong>The pipeline publishes what was merged, and only that.</strong> A merge into <code class="language-plaintext highlighter-rouge">drafts/linkedin/</code> on the main branch starts the publish workflow. It re-reads the draft, re-checks every gate, uploads the card image, creates the post, and writes the identifier LinkedIn returns into a ledger.</li>
  <li><strong>The ledger stops repeats.</strong> The ledger is keyed by the page’s address, so the same page never posts twice, whether the cause is a re-run or a second tool.</li>
</ol>

<p>The whole draft is a short file a reviewer can read in one screen:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">type</span><span class="pi">:</span> <span class="s">article</span>
<span class="na">status</span><span class="pi">:</span> <span class="s">pending</span>
<span class="na">source</span><span class="pi">:</span> <span class="s">tech/2026-10-29-automated-linkedin-posts-approved-by-a-person</span>
<span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Automated</span><span class="nv"> </span><span class="s">LinkedIn</span><span class="nv"> </span><span class="s">posts,</span><span class="nv"> </span><span class="s">approved</span><span class="nv"> </span><span class="s">by</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">person"</span>
<span class="nn">---</span>
<span class="s">The commentary goes here. This body is exactly what posts.</span>
</code></pre></div></div>

<h2 id="the-gates-and-what-each-one-stops">The gates, and what each one stops</h2>

<table>
  <thead>
    <tr>
      <th>Gate</th>
      <th>What it stops</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Not approved</td>
      <td>A draft nobody merged</td>
    </tr>
    <tr>
      <td>Publishing not armed</td>
      <td>Any run except the one publish step that is allowed to send. The switch lives in that step’s environment, and no file in the repository can turn it on</td>
    </tr>
    <tr>
      <td>Brand guard</td>
      <td>A banned phrase, or commentary over 3,000 characters</td>
    </tr>
    <tr>
      <td>Already published</td>
      <td>The same page posting twice</td>
    </tr>
    <tr>
      <td>Unconfirmed attempt</td>
      <td>A second try after LinkedIn errored mid-request, when the first may have posted. A person checks the page before anything is sent again</td>
    </tr>
    <tr>
      <td>No credential</td>
      <td>Every live call, until a page admin issues a token</td>
    </tr>
  </tbody>
</table>

<p>Two of these exist because of how writes fail. A request that dies halfway may still have created the post, so the pipeline never retries a write after a server error, and it records the doubt before doing anything else.</p>

<h2 id="no-model-is-load-bearing">No model is load-bearing</h2>

<p>The agent is optional. The first draft, the payload, the gates, and the ledger are plain code that behaves the same on every run. The model only improves text a person is about to read, and it never approves or publishes. That is <a href="/tools/partners/deterministic-first/" class="wiki-link" data-wiki-target="The deterministic-first doctrine">The deterministic-first doctrine</a> applied to a marketing channel: if the model is down or wrong, the worst outcome is a weaker draft waiting in a pull request.</p>

<h2 id="what-we-found-when-we-checked">What we found when we checked</h2>

<p>The question we asked was simple: if we merged a draft today, would it post? It would not have, for three reasons, and none of them showed up as a red check.</p>

<ul>
  <li><strong>One missing brace silenced the settings.</strong> In mid-September, a routine merge dropped a single closing brace from the site’s settings file. The publisher did not crash. It fell back to its defaults, a different queue folder and a stricter approval status, so a merged draft would have been answered with “Nothing to publish” on a green run. Only the status command treated the file as an error.</li>
  <li><strong>The tool moved.</strong> The content management system (CMS) behind the pipeline, our own open-source zer0-CMS, had been rewritten from Ruby to Python. The automated runs kept calling the right code, because they reach the CMS’s shared workflow by name. Every command a person runs locally, and the connection our AI assistant uses to draft posts, still pointed at a file that no longer existed.</li>
  <li><strong>The checks stayed green.</strong> The weekly token check skipped every Monday because no LinkedIn token was configured, and a skip reports as success. The weekly configuration check did catch the broken file. It flagged the file as invalid in seven consecutive weekly runs. It was set to report instead of fail, so all seven runs passed.</li>
</ul>

<h2 id="what-we-changed">What we changed</h2>

<ul>
  <li><strong>The configuration check can fail now.</strong> It fails on an error, and it also runs on any pull request that touches the settings files, so a broken file stops at review instead of landing on the main branch. Warnings still only report.</li>
  <li><strong>One script knows where the tool lives.</strong> A small wrapper is now the only file that knows where the CMS is checked out and which Python runs it. The drafting procedure, the command that starts it, the assistant’s connection, and the docs all call the wrapper, so the next move upstream is a one-line change.</li>
  <li><strong>We validated on a real post: this one.</strong> The status command reads the settings correctly, this article appears in the list of pages not yet shared, and the LinkedIn draft that shares it was written, previewed, and rehearsed locally through the same gates before anyone reviewed it.</li>
  <li><strong>The token stays a person’s job.</strong> LinkedIn access tokens last 60 days, and programmatic refresh is available only to a limited set of partners, per <a href="https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow">LinkedIn’s OAuth documentation</a>. A page admin issues the token by hand. With the app’s client ID and secret stored beside it, the weekly check fails a week before the token expires. Without them, it can only confirm the token still works. Until that token is in place, nothing posts. That is the gate doing its job.</li>
</ul>

<h2 id="watch-outs-if-you-build-this">Watch-outs if you build this</h2>

<ul>
  <li><strong>Read the skip notices.</strong> A scheduled check that skips for want of a credential usually shows green. Make a missing credential loud, or read the notice before you trust the badge.</li>
  <li><strong>Make a bad settings file fatal where it counts.</strong> A tool that falls back to defaults is friendly on a laptop and dangerous in production. Check the file in every pull request that changes it.</li>
  <li><strong>Plan for LinkedIn’s calendar.</strong> LinkedIn publishes a new marketing API version every month and supports each one for at least a year before retiring it, per its <a href="https://learn.microsoft.com/en-us/linkedin/marketing/versioning">versioning documentation</a>. Posting to a company page also needs API access LinkedIn reviews on its own schedule. Setting up the repository side typically takes days. Apply for access before you start.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If your business publishes good work that never leaves the website, we can set up the same pipeline for your company page, with your repository, your approval rules, and a token your own page admin holds. <a href="/contact/">Talk to us about governed AI automation</a>.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="tech" /><category term="ai" /><category term="automation" /><category term="governance" /><category term="agentic-ai" /><category term="devops" /><category term="smb" /><category term="linkedin" /><summary type="html"><![CDATA[Automate LinkedIn company-page posts and keep a person approving each one: drafts as files, a merge as the sign-off, and checks that go red when it breaks]]></summary></entry><entry><title type="html">Azure DevOps for small teams: a practical guide</title><link href="https://www.bash-365.com/posts/2026/10/16/azure-devops-guide-for-small-teams/" rel="alternate" type="text/html" title="Azure DevOps for small teams: a practical guide" /><published>2026-10-16T18:00:00+00:00</published><updated>2026-10-16T18:00:00+00:00</updated><id>https://www.bash-365.com/posts/2026/10/16/azure-devops-guide-for-small-teams</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/10/16/azure-devops-guide-for-small-teams/"><![CDATA[<p>Most small teams outgrow shared drives and email threads long before they outgrow their budget. Work lives in spreadsheets, code lives in one person’s laptop, and releases happen by hand on Friday afternoon. Azure DevOps gives you planning, source control, and automated builds in one place. The first five people are free.</p>

<h2 id="why-it-matters-now">Why it matters now</h2>

<p>Manual releases create downtime risk. When the person who knows the deploy steps is out, the release waits. When there is no shared view of what is in flight, owners see status only after something slips. Azure DevOps reduces both problems without requiring a large platform team.</p>

<p>For a team of five or fewer, the Basic tier covers Boards, Repos, Pipelines, and Artifacts at no charge. Additional users cost $6 each per month. Microsoft-hosted pipelines include 1,800 minutes per month on one parallel job. That is enough for most small applications if builds stay under roughly 15–20 minutes.</p>

<h2 id="what-we-would-actually-do">What we would actually do</h2>

<p>Start with one organization and one project. Pick the Scrum process template unless the team already runs a different cadence. Keep the default work item types (User Story, Bug, Task) and the default states. Add custom fields only after the team has used the board for a full sprint and can name a concrete gap.</p>

<p>In Repos, enable branch policies on <code class="language-plaintext highlighter-rouge">main</code>: require a pull request, require at least one reviewer, and require the pipeline to pass. That single change stops most accidental direct pushes.</p>

<p>In Pipelines, start with a YAML file that builds the application, runs the unit tests that already exist, and publishes the artifact. Deploy to a non-production environment first. Add production only after the team has seen one clean pipeline run.</p>

<h2 id="how-it-plays-out">How it plays out</h2>

<p>A small team can reach a usable setup in one to two weeks:</p>

<ol>
  <li>Create the organization and project (same day).</li>
  <li>Move the existing repository into Azure Repos or connect an existing GitHub repository to Pipelines.</li>
  <li>Write the first YAML pipeline and get one successful build.</li>
  <li>Turn on the branch policy and run one full pull-request cycle.</li>
  <li>Invite stakeholders as free Stakeholder users so they can see the board without a paid license.</li>
</ol>

<p>The team’s ongoing work is updating work items when status changes and reviewing the occasional failed pipeline. That is lighter than maintaining a separate project tool, a separate Git host, and a separate build server.</p>

<h2 id="watch-outs">Watch-outs</h2>

<p><strong>Process customization.</strong> Adding states, fields, and rules early creates reporting debt. Wait until the team can point to a specific decision that the extra data would change.</p>

<p><strong>Pipeline minutes.</strong> Long builds or frequent runs can exhaust the free 1,800 minutes. Self-hosted agents are available (one free parallel job) and often cheaper once the team has a machine that can stay online.</p>

<p><strong>Mixing with GitHub.</strong> Azure DevOps Pipelines can build from GitHub repositories. If the team already lives in GitHub for code review, keep the code there and use Azure DevOps only for Boards and Pipelines rather than migrating twice.</p>

<p><strong>Test Plans.</strong> Manual test case management sits in the Basic + Test Plans tier at $52 per user per month. Most small teams do not need it at the start.</p>

<h2 id="next-step">Next step</h2>

<p>If you want help deciding whether Azure DevOps fits your current stack, or if you want a lean setup reviewed before the team starts customizing, <a href="/contact/">contact us</a> or review our <a href="/services/cloud/">cloud architecture</a> and <a href="/services/dev/">custom software</a> services.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="tech" /><category term="azure" /><category term="devops" /><category term="ci-cd" /><category term="project-management" /><category term="small-teams" /><summary type="html"><![CDATA[How a small team can use Azure DevOps to track work, control code, and automate releases without overbuilding process or overspending.]]></summary></entry><entry><title type="html">Hold the major: a software update policy you can sign off on</title><link href="https://www.bash-365.com/posts/2026/10/15/hold-the-major-a-software-update-policy-you-can-sign-off-on/" rel="alternate" type="text/html" title="Hold the major: a software update policy you can sign off on" /><published>2026-10-15T12:00:00+00:00</published><updated>2026-10-15T12:00:00+00:00</updated><id>https://www.bash-365.com/posts/2026/10/15/hold-the-major-a-software-update-policy-you-can-sign-off-on</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/10/15/hold-the-major-a-software-update-policy-you-can-sign-off-on/"><![CDATA[<p>Every business that runs software eventually picks an update policy, usually by accident. Either everything updates automatically and something breaks on a Tuesday, or nothing updates until a security scanner turns the dashboard red. Both are policies. Neither is one you’d sign off on if someone wrote it down.</p>

<h2 id="the-risk-is-one-rule-for-three-different-kinds-of-change">The risk is one rule for three different kinds of change</h2>

<p>A patch that closes a published vulnerability, a minor release with a new feature, and a major release that changes how the product works are three different business decisions. Treating them the same is the mistake. “Update everything” puts a breaking change on the same fast track as a security fix. “Freeze everything” holds the security fix hostage to the fear of the breaking change.</p>

<p>We saw this play out in our own portfolio in the first two weeks of October. The repositories behind our sites and tools use GitHub’s Dependabot to propose dependency updates automatically, grouped into one pull request per batch to cut down the noise. Three times in one week, a single incompatible major version took a whole batch down with it:</p>

<ul>
  <li>In one application, a TypeScript 5 to 7 upgrade could not install at all, because the linting toolkit it sits beside only accepts TypeScript versions below 6.1 (<a href="https://github.com/bamr87/aieo/pull/115">aieo#115</a>).</li>
  <li>In another, a testing library’s version 3 conflicted with the test runner, which still requires version 2. The whole grouped update failed at install, and closing it did nothing, because the tool recreated the same batch with the same bad upgrade in it (<a href="https://github.com/bamr87/law-ai/pull/153">law-ai#153</a>).</li>
  <li>A day later, in the same application, ESLint 10 removed a function that a widely used React plugin still calls, so the next regrouped batch failed at the lint step instead (<a href="https://github.com/bamr87/law-ai/pull/157">law-ai#157</a>).</li>
</ul>

<p>Nothing here is exotic. Every one of those was a vendor shipping a major release before the rest of its ecosystem caught up. That’s normal. What turns it into downtime is a policy that can’t tell it apart from a routine patch.</p>

<h2 id="what-wed-actually-do-three-lanes-each-with-an-owner">What we’d actually do: three lanes, each with an owner</h2>

<p><strong>Lane 1: security fixes go fast.</strong> When a published vulnerability has a fixed version, it moves in days, not at the next maintenance window. In the same fortnight, a Denial of Service (DoS) advisory against Django turned a project’s dependency scan red; the fix was a one-step patch bump within the same 5.2 line (<a href="https://github.com/bamr87/barodybroject/pull/196">barodybroject#196</a>). Another project raised the minimum allowed version of its test runner to clear a security alert, without pinning it to one exact release (<a href="https://github.com/bamr87/bamr87/pull/356">bamr87#356</a>). Neither change needed a meeting.</p>

<p><strong>Lane 2: minor and patch updates flow automatically, with tests as the gate.</strong> These are the updates the “update everything” crowd is right about. If your tests pass, take them. Holding them back is how you end up three years behind and facing one enormous upgrade instead of thirty small ones.</p>

<p><strong>Lane 3: major versions are held on purpose, with a written exit condition.</strong> This is the lane most businesses skip. A hold is not “we’ll get to it.” It’s a line in the configuration that names the product, the version being held, the reason, and the signal that ends the hold. In each of the three cases above, the fix was a short <code class="language-plaintext highlighter-rouge">ignore</code> rule in the Dependabot configuration file, which GitHub documents as able to skip a single semantic-versioning level such as major updates (<a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference">Dependabot options reference</a>). Each rule carries a comment saying when to remove it, for example “drop this once the React plugin supports ESLint 10,” and each hold is logged in the project’s backlog so it doesn’t become permanent by neglect.</p>

<p>The same principle applies to your own shared tools. Our sites all run one shared quality-check workflow. Until this month, most of them pointed at its live development copy, so any change to it reached every site at once. When a stricter version shipped, we released it under a version label, <code class="language-plaintext highlighter-rouge">v2</code>, and moved each site to that label through its own reviewable change (<a href="https://github.com/bamr87/bamr87/pull/354">bamr87#354</a> and the follow-up pin pull requests). Now a change to the shared tool reaches a site only when that site opts in.</p>

<h2 id="how-it-plays-out">How it plays out</h2>

<p>For a typical small business, this is a policy document and a few hours of configuration, not a project.</p>

<ol>
  <li><strong>Inventory what updates itself.</strong> Your line-of-business apps, your Microsoft 365 or Google Workspace tenant, endpoint software, and any custom code. Note which of them can be held at a major version and which can’t. Software-as-a-Service (SaaS) vendors often decide this for you.</li>
  <li><strong>Write the three lanes down.</strong> One page. Who approves a security fix (usually nobody beyond whoever applies it), what testing gates a minor update, and who decides when a major hold ends.</li>
  <li><strong>Turn holds into records.</strong> Every held major gets a reason and an exit condition, in the tool’s configuration where possible and on your IT ledger either way. Review them at a set interval, the way finance reviews open items at month-end close.</li>
  <li><strong>Pin shared things to versions, not to “latest.”</strong> Scripts, templates, and integrations that more than one system depends on should move by deliberate release, so one upstream change can’t break every downstream system on the same morning.</li>
</ol>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>A hold with no exit condition is a freeze.</strong> Six months later nobody remembers why, and you’re now facing the exact large, risky upgrade the policy was meant to avoid.</li>
  <li><strong>Don’t let a major hold block a security fix.</strong> Check that your tooling still delivers security updates for held products, and if a vulnerability is only fixed in the new major, the hold ends now, on purpose.</li>
  <li><strong>Closing the noisy update isn’t the same as deciding.</strong> As the testing-library case showed, an automated tool will often re-propose the same change. Write the decision into the configuration, or you’ll make it again every week.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If your business runs on software that updates itself on someone else’s schedule, the policy above is a one-afternoon conversation and a short document. Our <a href="/services/managed-it/" class="wiki-link" data-wiki-target="Managed IT services">Managed IT services</a> engagements start with exactly this kind of inventory, or <a href="/contact/">get in touch</a> and we’ll walk through your update lanes with you.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="corp" /><category term="change-management" /><category term="patch-management" /><category term="it-governance" /><category term="vendor-lock-in" /><category term="risk-management" /><summary type="html"><![CDATA[A three-lane software update policy that keeps security fixes fast, routine updates automatic, and breaking upgrades on your schedule]]></summary></entry><entry><title type="html">The future of manufacturing ERP is a Linux distribution</title><link href="https://www.bash-365.com/posts/2026/10/03/the-future-of-manufacturing-erp-is-a-linux-distribution/" rel="alternate" type="text/html" title="The future of manufacturing ERP is a Linux distribution" /><published>2026-10-03T19:45:00+00:00</published><updated>2026-10-03T19:45:00+00:00</updated><id>https://www.bash-365.com/posts/2026/10/03/the-future-of-manufacturing-erp-is-a-linux-distribution</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/10/03/the-future-of-manufacturing-erp-is-a-linux-distribution/"><![CDATA[<p>For the last decade, the loudest Enterprise Resource Planning (ERP) roadmaps have pointed in the same direction: up, into the vendor’s cloud. For a law firm or a distributor, that direction is mostly fine. For a manufacturer, the roadmap runs into a fact no slide deck can move: the machines are bolted to the floor in the building, and the ERP has to talk to them.</p>

<p>So here is my position, stated plainly so you can argue with it. The future of ERP is open source, shipped the way Linux is shipped: a shared core, with vendor- and industry-specific distributions built on top. And for manufacturers in particular, a true ERP cannot run efficiently or economically as pure Software as a Service (SaaS). It has to live close to the shop floor, which means it has to live comfortably on the same open Linux stack that already runs the industrial edge.</p>

<p>That is an opinion, not a forecast with a confidence interval. Below is the case for it, the strongest arguments against it, what artificial intelligence (AI) coding agents change about those arguments, and the places where I think it still needs a caveat.</p>

<h2 id="linux-already-solved-the-distribution-problem">Linux already solved the distribution problem</h2>

<p>The way I read the history, Linux did not win by being one product. It won by being one <em>core</em> that many organizations could package for different needs. Debian is a distribution; <a href="https://www.debian.org/derivatives/">Ubuntu is one of many distributions built on Debian’s work</a>; Red Hat Enterprise Linux (RHEL) is a commercial distribution with a support subscription attached. The kernel underneath is shared. What differs is curation, release cadence, defaults, and who answers the phone at 2 a.m.</p>

<p>ERP has most of the parts for the same model already sitting on the shelf:</p>

<table>
  <thead>
    <tr>
      <th>Project</th>
      <th>Core license</th>
      <th>What it shows</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://github.com/frappe/erpnext">ERPNext</a> on the <a href="https://github.com/frappe/frappe">Frappe Framework</a></td>
      <td>ERPNext: <a href="https://github.com/frappe/erpnext/blob/develop/license.txt">GNU GPL v3</a>; Frappe: <a href="https://github.com/frappe/frappe/blob/develop/LICENSE">MIT</a></td>
      <td>A framework-plus-application split, close to a kernel-plus-userland split. Manufacturing is in the core feature list.</td>
    </tr>
    <tr>
      <td><a href="https://www.odoo.com/documentation/18.0/legal/licenses.html">Odoo</a></td>
      <td>Community: GNU LGPL v3; Enterprise: proprietary</td>
      <td>Open core. The open edition is real, and the paid edition is not open source.</td>
    </tr>
    <tr>
      <td><a href="https://odoo-community.org/">Odoo Community Association</a></td>
      <td>Community modules</td>
      <td>Lists 20,000-plus community-built Odoo modules, which is a package repository in all but name.</td>
    </tr>
    <tr>
      <td><a href="https://ofbiz.apache.org/">Apache OFBiz</a></td>
      <td><a href="https://github.com/apache/ofbiz-framework/blob/trunk/LICENSE">Apache License 2.0</a></td>
      <td>An Apache top-level project since 2006, with manufacturing and material requirements planning (MRP) modules.</td>
    </tr>
    <tr>
      <td><a href="https://www.idempiere.org/">iDempiere</a></td>
      <td><a href="https://github.com/idempiere/idempiere/blob/master/LICENSE.md">GNU GPL v2</a></td>
      <td>Community-run since 2011, with long-term support (LTS) releases.</td>
    </tr>
    <tr>
      <td><a href="https://www.dolibarr.org/">Dolibarr</a></td>
      <td><a href="https://github.com/Dolibarr/dolibarr/blob/develop/COPYING">GNU GPL v3</a></td>
      <td>Describes itself as a “multi-distribution model”: on-premise, self-hosted cloud, or SaaS.</td>
    </tr>
  </tbody>
</table>

<p>What’s mostly missing is the step Linux took in the 1990s: the <em>industry distribution</em>. Think of it as Ubuntu for job shops. A curated, tested bundle of the core, the manufacturing modules, the U.S. tax and payroll localizations, and the machine connectors, released on a predictable cadence, with a vendor you can pay for support and leave if you need to. Nobody types <code class="language-plaintext highlighter-rouge">apt install erp</code> and gets a running plant. But someone can maintain the equivalent of a well-tested package list for a 60-person Front Range machine shop, and charge for doing it well.</p>

<p>That is the first half of the thesis. The second half is about where the server sits.</p>

<h2 id="why-the-shop-floor-keeps-the-erp-close">Why the shop floor keeps the ERP close</h2>

<p>A manufacturing ERP is not just a ledger with a warehouse attached. The transactions that matter most, like backflushing material, recording lot and serial genealogy, putting a quality hold on a batch, or reporting scrap against a work order, come from machines rather than from someone with a clipboard in a well-connected plant. The International Society of Automation publishes a whole standard on this boundary, <a href="https://www.isa.org/standards-and-publications/isa-standards/isa-95-standard">ISA-95, Enterprise-Control System Integration</a>, because getting it wrong is how plants end up re-keying production by hand.</p>

<p>The machines speak open industrial protocols. Open Platform Communications Unified Architecture (OPC UA) is the <a href="https://opcfoundation.org/about/opc-technologies/opc-ua/">platform-independent framework from the OPC Foundation</a>, and <a href="https://www.open62541.org/">open62541</a> is an open-source implementation of it under the Mozilla Public License 2.0 that runs on Linux, real-time operating systems, and other platforms. <a href="https://mqtt.org/">MQTT is an OASIS-standard publish/subscribe protocol</a>, and the Eclipse Foundation’s <a href="https://sparkplug.eclipse.org/">Sparkplug specification</a> defines how industrial data rides on it. Its client libraries and reference implementations, <a href="https://github.com/eclipse-tahu/tahu">Eclipse Tahu</a>, are open source too. The protocols at the bottom of the plant are already open. The question is whether the ERP at the top meets them on equal terms.</p>

<p>Three forces push that meeting point toward the plant instead of a distant cloud region:</p>

<ul>
  <li><strong>Offline resilience.</strong> When the internet connection drops, the line keeps running. If the ERP can’t receive production, the plant has to choose between stopping and keeping paper records to re-key later, and paper is where genealogy and inventory accuracy go to die.</li>
  <li><strong>Latency and determinism.</strong> Programmable logic controllers (PLCs) and computer numerical control (CNC) machines run their own control loops, and nobody sensible puts those in the ERP. But the layer that turns machine events into transactions, such as the manufacturing execution system (MES), the historian, and the ERP’s production module, works better when the network path is one you own and can troubleshoot.</li>
  <li><strong>Data volume and egress.</strong> Sending data into a public cloud is generally free, and <a href="https://azure.microsoft.com/en-us/pricing/details/bandwidth/">Azure’s pricing FAQ says as much for inbound transfer</a>. Getting it back out is not. Microsoft’s published Azure bandwidth pricing (checked October 2026) lists internet egress from North America over its own network as free for the first 100 GB a month, then about $0.087 per GB for the next 10 TB. In fairness, AWS says <a href="https://aws.amazon.com/blogs/aws/free-data-transfer-out-to-internet-when-moving-out-of-aws/">over 90 percent of its customers pay no data-transfer-out charges at all</a> because of a similar 100 GB monthly allowance. Egress is not a tax on everyone. It is a tax on exactly the data-heavy, machine-connected plant this essay is about, especially one that pulls sensor history back down to an on-site MES or a second platform every day.</li>
</ul>

<p>And the Linux side of the stack has spent the last few years becoming more industrial. In <a href="https://kernelnewbies.org/Linux_6.12">Linux 6.12, released November 17, 2024</a>, the real-time preemption patch set (PREEMPT_RT) was merged into mainline after roughly 20 years of work, so a real-time-capable kernel is now a configuration option, not an out-of-tree patch. The Linux Foundation’s <a href="https://www.cip-project.org/">Civil Infrastructure Platform</a> maintains super-long-term kernels with a ten-year maintenance period, aimed at industrial automation and infrastructure, the kind of timescale plant equipment lives on. <a href="https://elisa.tech/">ELISA</a> is working on tools and processes for certifying Linux in safety-critical systems. <a href="https://www.lfedge.org/">LF Edge</a> hosts open edge-computing projects across industrial manufacturing and other sectors. And <a href="https://margo.org/">Margo</a>, also under the Linux Foundation, is writing an open standard for running edge applications across vendors, with statements of support on its site from ABB, Rockwell Automation, Schneider Electric, Siemens, and Microsoft. Margo is still in preview releases, so treat it as a direction of travel, not a finished standard.</p>

<p>Be precise about what that does and doesn’t prove. Your ERP does not need a real-time kernel. Hard control stays on the PLC. The point is that one open kernel family now credibly covers everything from the edge controller to the ERP server. That means one operating-system skill set, one patching pipeline, and one set of open protocols from the sensor to the general ledger (GL). An ERP built to live on that stack fits the plant. An ERP that can only live in its vendor’s region has to be bolted onto the plant through a gateway.</p>

<h2 id="the-strongest-objections-taken-seriously">The strongest objections, taken seriously</h2>

<h3 id="hybrid-already-solves-this-saas-erp-plus-an-edge-gateway">“Hybrid already solves this: SaaS ERP plus an edge gateway”</h3>

<p>This is the best counterargument, and in many plants it’s the right architecture. Microsoft describes <a href="https://learn.microsoft.com/en-us/azure/iot-operations/overview-iot-operations">Azure IoT Operations</a> as a data plane for the edge. It runs on Azure Arc-enabled Kubernetes, includes an edge MQTT broker, supports OPC UA, and processes data locally before sending it to the cloud. Even Odoo’s own approach is an <a href="https://www.odoo.com/documentation/18.0/applications/general/iot/iot_box.html">Internet of Things (IoT) box</a>, a local gateway that needs its own subscription on top of the Odoo one, which Odoo says is designed to give your database access to devices on your local network. Hybrid is real, it ships, and it works.</p>

<p>Notice two things, though. First, look at what the hybrid answer actually is: Kubernetes and an MQTT broker running <em>at the plant</em>. The hard part already lives on Linux on your side of the internet connection. Once you’re operating that, I’d argue the extra cost of running the transactional core nearby is smaller than the “cloud versus on-premise” debate suggests. Second, the gateway sets the limits. Microsoft’s own documentation says Azure IoT Operations can operate offline for a maximum of 72 hours, with possible degradation in that window. That’s an honest, useful number, and it’s a design limit you now have to plan around. A three-day holiday weekend with a fiber cut is not a hypothetical in rural Colorado. The gateway is also the vendor’s, so lock-in doesn’t disappear. It moves to the edge.</p>

<p><strong>The caveat I’ll concede:</strong> for an assembly shop where production is reported by people at terminals rather than by machines, SaaS ERP plus a light gateway is a perfectly sound choice, and I wouldn’t talk anyone out of it. The thesis is strongest where machine data drives transactions.</p>

<h3 id="open-source-isnt-free">“Open source isn’t free”</h3>

<p>Correct. The license fee goes away, but the implementation, data migration, training, hosting, security patching, upgrades, and support do not. Some well-known projects are open core: Odoo’s Enterprise edition, for example, can only be used with a paid subscription. In our experience the license is rarely the line that sinks an ERP budget. The people-hours are.</p>

<p>Here’s why the thesis survives anyway. Open source changes <em>who you’re allowed to pay</em> for that work and <em>what you keep</em> when you stop paying. The distribution model is the business answer to “isn’t free”: you buy a support subscription from the distro vendor, the way companies buy RHEL, but the code, the data model, and the exit stay yours. A total cost of ownership (TCO) comparison that counts the license line and ignores the switching cost is measuring the wrong thing.</p>

<p><strong>The caveat:</strong> if you have no in-house information technology (IT) capacity and no budget for a partner, a well-run SaaS ERP can cost less for years, and “we own the code” is cold comfort when nobody on staff can read it. Open source moves cost from licenses to people. Plan for the people. AI coding agents shrink that people cost, which I’ll come back to below, but they don’t remove it.</p>

<h3 id="a-50-person-plant-cant-out-secure-a-hyperscaler">“A 50-person plant can’t out-secure a hyperscaler”</h3>

<p>Also fair. Self-hosting means owning the patches, the backups, and the incident response. Two answers. First, open source does not have to mean on-premise: managed hosting for open-source ERP exists, from <a href="https://frappe.io/cloud">Frappe Cloud</a> for ERPNext to local managed service providers, and Dolibarr explicitly offers on-premise, self-hosted, or SaaS. You choose where it runs and who runs it, and you can change your mind. Second, the edge stack you need for machine integration has to be secured either way. Siting the ERP next to it doesn’t add a new kind of risk. It adds one more workload to a discipline you already need.</p>

<h3 id="there-arent-enough-consultants">“There aren’t enough consultants”</h3>

<p>As far as we can tell, there are fewer implementers for ERPNext or iDempiere than for the big proprietary suites, and that is a real risk for a buyer. It’s also the clearest sign that the industry distribution is the missing piece. A distro vendor’s whole job is to make a long tail of modules into something a smaller partner network can support. AI coding agents work on the same gap from the other side: a small partner with agents and a readable codebase can plausibly cover more plants than it could before.</p>

<h2 id="what-ai-coding-agents-change-about-the-people-problem">What AI coding agents change about the people problem</h2>

<p>Three of the concessions above come down to the same constraint: people. Open source moves cost from licenses to labor, implementers for open-source ERP are thin on the ground, and a plant with no technical staff can’t own code nobody can read. That’s also the constraint that has been moving fastest.</p>

<p>AI coding agents now do supervised work on real codebases, well beyond autocomplete. GitHub says its <a href="https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent">Copilot cloud agent</a> can research a repository, plan changes, fix bugs, implement incremental features, and improve test coverage. It works in its own temporary environment where it can run tests and linters, and it hands back a branch or a pull request for a person to review. Anthropic describes <a href="https://code.claude.com/docs/en/overview">Claude Code</a> as a tool that reads your codebase, edits files, and runs commands. And METR, a nonprofit research group, <a href="https://metr.org/blog/2026-02-24-uplift-update/">reported in February 2026</a> that use of agentic tools such as Claude Code and Codex grew among open-source developers through 2025. A growing share of the developers it tried to recruit for a productivity study said they would not want to do half their work without AI.</p>

<p>For a manufacturer on an open-source ERP, that capability lands on exactly the work the earlier caveats worried about:</p>

<ul>
  <li><strong>Connectors.</strong> An adapter that subscribes to Sparkplug topics on the plant’s MQTT broker, or reads OPC UA nodes through open62541, and posts counts and scrap to an ERPNext or Odoo work order.</li>
  <li><strong>Plant-specific modules.</strong> A Frappe app or an Odoo module for an inspection form, a scrap-reason code list, or a label format your biggest customer insists on.</li>
  <li><strong>Reports and migrations.</strong> The report the controller has been rebuilding in a spreadsheet every month, and the scripts that move item masters and open orders out of the old system.</li>
  <li><strong>Tests and upgrades.</strong> Tests written with each change, and, when the next major version ships, a first pass at reading the upstream changes and proposing fixes for what broke.</li>
</ul>

<p>Here’s why this strengthens the open-source case specifically, not just ERP in general. An agent can only work with what it can read. An open-source ERP gives it the whole codebase, the data model, and the project’s history of changes. Open protocols give it a published specification and an open reference implementation to test against. A proprietary SaaS ERP gives it an application programming interface (API) at best, with a black box behind it. The agent can call the API, but it can’t read why the vendor’s posting logic behaves the way it does or predict what next quarter’s release will change. The main public benchmark for these agents, <a href="https://www.swebench.com/">SWE-bench</a>, is itself built from real GitHub issues in open-source Python repositories. That’s telling: open code is where agents can see the code, the issue, and the tests all at once. Open code plus open protocols is the environment agents work best in, and it’s the environment this essay argues manufacturers should be choosing anyway.</p>

<p>Keep the claim the right size. I’m not offering a productivity multiplier, because the best evidence doesn’t support a clean one. METR’s <a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/">randomized trial in early 2025</a> found that experienced open-source developers took 19% longer with AI tools than without, even though they believed they had been faster. METR now says those results are out of date. Its February 2026 update says developers are likely sped up more now, but that its newer data is only very weak evidence of how much. The narrower claim I’ll stand behind: agents lower the floor. They reduce the specialist headcount it takes to own and maintain a modest set of connectors, modules, and reports on an open codebase. They don’t make the work free, and they don’t make it safe by default.</p>

<p>The limits are where most of the risk lives:</p>

<ul>
  <li><strong>A human owner still reviews and merges.</strong> Every agent change should arrive as a pull request that a named person who understands the plant reads, tests, and approves. That’s the governance pattern from our earlier post, <a href="/posts/2026/09/13/erp-customizations-should-be-the-default/" class="wiki-link" data-wiki-target="ERP customizations should be the default">ERP customizations should be the default</a>, applied to the shop floor. An agent with no reviewing owner is not a team. It’s an unattended commit history.</li>
  <li><strong>Agents get shop-floor safety logic wrong.</strong> A model doesn’t know that a guard interlock exists, what your lockout procedure is, or why the line has to stop when a sensor goes quiet. Control and safety logic stay on the PLC, under the people and standards that already govern it. The agent’s work ends where machine events become transactions. It writes to the ERP, never to the controller.</li>
  <li><strong>Security and supply chain.</strong> A <a href="https://arxiv.org/abs/2406.10279">2024 study of 16 code-generating models and 576,000 code samples</a> found that, on average, at least 5.2% of the packages suggested by commercial models and 21.7% of those suggested by open-source models did not exist. Each fake name is an opening for an attacker who registers it. Pin dependencies, review every new package, run agents with least privilege, and keep them away from production credentials and the plant network.</li>
  <li><strong>Agents don’t replace process knowledge.</strong> An agent doesn’t know why you backflush at the operation instead of at completion, or which customer’s lot rules live in a contract rather than in the system. That knowledge is the business, and it still lives in people. Agents shrink the coding, not the knowing.</li>
</ul>

<p><strong>The updated caveat:</strong> the “SaaS until you have the people” advice still holds for a plant with nobody technical at all. But “the people” is now a smaller number than it was. For many small plants, one technically minded owner plus a part-time partner, working with agents under review, can plausibly own a modest open-source deployment that would have needed a dedicated developer a few years ago. That’s a judgment, not a measured result, so test it on your own work before you bet on it.</p>

<h2 id="where-the-thesis-holds-and-where-it-bends">Where the thesis holds, and where it bends</h2>

<table>
  <thead>
    <tr>
      <th>Plant profile</th>
      <th>My read</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Machine-connected production: genealogy, overall equipment effectiveness (OEE), automated backflush, regulated lots</td>
      <td>Strongest case. Plan for ERP and MES near the floor on Linux, on an open-source core, with cloud for analytics and backup.</td>
    </tr>
    <tr>
      <td>Mixed: some machine data, mostly people reporting at terminals</td>
      <td>Either path works. Choose on exit cost and offline tolerance, not on the demo.</td>
    </tr>
    <tr>
      <td>Light assembly or make-to-order with manual reporting</td>
      <td>SaaS ERP plus a gateway is fine. Keep your data exportable.</td>
    </tr>
    <tr>
      <td>One technically minded owner and a small partner budget</td>
      <td>Now plausible, which it mostly wasn’t before. Run an open-source core, with AI coding agents drafting connectors, modules, and reports, and every change reviewed by that owner or the partner.</td>
    </tr>
    <tr>
      <td>Nobody technical and no partner budget</td>
      <td>SaaS first, until you have at least one person who can review changes. Open source without owners is just abandoned software you happen to have a copy of, and agents without a reviewer make it worse, faster.</td>
    </tr>
  </tbody>
</table>

<h2 id="what-to-do-this-quarter">What to do this quarter</h2>

<p>You don’t have to bet the plant on my prediction to act on it. These steps pay off whichever way it goes:</p>

<ul>
  <li><strong>Map the machine-to-ledger paths.</strong> List every transaction that starts as a machine signal, such as counts, scrap, temperatures that trigger holds, or labels, and note how it reaches the ERP today. That list is your real integration scope.</li>
  <li><strong>Measure your offline tolerance.</strong> Ask operations: if the internet connection dropped at 6 a.m. Friday, what stops, and when? Compare the answer with what your current or proposed ERP can actually do offline, in writing.</li>
  <li><strong>Price egress against your own volumes.</strong> Use the cloud vendor’s pricing page, not a sales estimate. Count data leaving the cloud, not just data entering it.</li>
  <li><strong>Ask every ERP vendor four questions.</strong> What runs at the plant when the link is down? What open protocols, such as OPC UA or MQTT with Sparkplug, does it speak natively? Under what license are the edge components? In what format, and at what cost, do we get all our data out?</li>
  <li><strong>Stand up a sandbox.</strong> Put an open-source ERP (Odoo publishes <a href="https://www.odoo.com/documentation/18.0/administration/on_premise/packages.html">packaged installers for Debian and Ubuntu</a>, and ERPNext’s Frappe Framework <a href="https://docs.frappe.io/framework/user/en/installation">officially supports Debian and Ubuntu</a>) on a spare Linux box, load one product line’s bill of materials, and see how far you get in a few weeks. Then ask an AI coding agent to write one small connector or report against that open code, and have someone review the pull request it produces. That review will tell you more about your real staffing need than any vendor’s estimate. The point is to see what’s possible and to build your negotiating position, not to replace your system. For what happens when that kind of evaluation never takes place, see <a href="/news/erp/frankenstein-erp-legacy-fragmentation/" class="wiki-link" data-wiki-target="Frankenstein&#39;s ERP: the monster you already own">Frankenstein’s ERP: the monster you already own</a>.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>Linux didn’t win because anyone mandated it. In my reading, it won because a shared core with many distributions turned out to be the cheapest way to fit a huge range of needs without handing any one vendor the keys. I think manufacturing ERP is heading the same way, for the same reason, with the added push of physics: the machines are in your building. If you’re weighing an ERP decision for a plant and want a vendor-neutral second opinion on where it should run, what it should speak, and how much of the work agents can carry, see our <a href="/services/erp/">ERP consulting for manufacturers</a>.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="muses" /><category term="open-source" /><category term="manufacturing" /><category term="linux" /><category term="edge-computing" /><category term="erp-implementation" /><category term="vendor-lock-in" /><category term="total-cost-of-ownership" /><category term="agentic-ai" /><summary type="html"><![CDATA[Manufacturers can cut lock-in and cloud data costs with open-source ERP on Linux near the shop floor, and AI coding agents now shrink the team it needs]]></summary></entry><entry><title type="html">IT is the new finance department</title><link href="https://www.bash-365.com/posts/2026/09/14/it-is-the-new-finance-department/" rel="alternate" type="text/html" title="IT is the new finance department" /><published>2026-09-14T12:00:00+00:00</published><updated>2026-09-14T12:00:00+00:00</updated><id>https://www.bash-365.com/posts/2026/09/14/it-is-the-new-finance-department</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/09/14/it-is-the-new-finance-department/"><![CDATA[<p>For most of business history there was no finance department. There was a ledger, and a clerk who kept it, and an owner who could see everything else by walking the floor. Finance became a <em>department</em> — a function with authority over every other function — only when companies grew past what an owner could see. Alfred Chandler’s <em>The Visible Hand</em> (1977) traces the modern management hierarchy, and modern cost accounting with it, to the railroads of the 1850s: the first businesses so large and so spread out that the only way to run them was through the numbers. The ledger stopped being a record of the business and became the only complete view of it.</p>

<p>That is where finance’s power comes from. Not from the money — from the <em>seeing</em>. Finance holds the system of record, so every other department reports to it, in its language, on its calendar. The chief financial officer (CFO) sits with the owner because the CFO is the one person in the building who can describe the whole company in a single unit of account.</p>

<p>Hold that definition — the department that can see the whole company at once — and ask who fits it now. In a small or medium business (SMB) in 2026, it isn’t finance. It’s information technology (IT), and almost nobody has told IT.</p>

<h2 id="the-ledger-moved">The ledger moved</h2>

<p>Ask a simple question of a 40-person company: when something happens, where does it get <em>recorded</em>? A sale lands in the customer relationship management (CRM) system. A hire lands in the payroll platform and the identity directory. A purchase order lands in the Enterprise Resource Planning (ERP) system or, if you’re honest, in an email thread. A contract lands on the shared drive. A decision lands in a chat channel. Every one of those events exists in a system, with a timestamp and a username, long before it becomes a journal entry.</p>

<p>The general ledger is still the system of record for money. But somewhere along the way it became a <em>downstream</em> report — a summary, produced monthly, of things the operational systems already knew in real time. For a century the ledger was the source and the operation was what it described. Now the operation is data, and the ledger is one of its outputs.</p>

<p>Whoever runs the systems runs the record. The auditors noticed first. After the <a href="https://www.govinfo.gov/app/details/PLAW-107publ204">Sarbanes-Oxley Act of 2002</a>, an audit of a public company’s financial statements came to include the systems those statements come out of: who can log in, who can change a number, whether the change leaves a trace. The logic was unavoidable. A control that lives in software is only as strong as the software’s permissions, so the auditor has to test the permissions. A private company never files under that statute, but the same questions reach it anyway, on the cyber-insurance renewal and in a large customer’s due-diligence questionnaire. Finance has been sharing its authority with IT ever since, whether or not the org chart admits it.</p>

<h2 id="follow-the-spend">Follow the spend</h2>

<p>Finance’s second source of authority is budget. Spend passes through finance, so finance sets the rules for spend. Now look at where a small company’s operating spend has gone.</p>

<p>It has gone to subscriptions. Software as a service (SaaS) seats, cloud storage and compute, and, increasingly, metered artificial intelligence (AI) — priced per seat, per gigabyte, per token, billed monthly, and expanding by default. Most of it never crosses a purchasing desk. It accrues on corporate cards across the company, approved by whoever had the card. Software bought outside IT — shadow IT — is the expense-report problem of this decade: petty cash, except it renews automatically and has your customer list inside it. In the companies we walk into, the real subscription list is longer than the owner’s guess, and it is rarely shorter.</p>

<p>AI sharpens the point. An agent that reads a shared inbox, drafts responses, and files the results is not a tool in the way a spreadsheet is a tool. It is a worker with a login, a set of permissions, and a budget that meters like electricity. Someone has to provision the identity, scope what it can touch, cap what it can spend, and keep a record of what it did on the company’s behalf. That is finance’s job description — custody, authorization, limits, audit trail — applied to software. It lands on IT because it can’t land anywhere else.</p>

<h2 id="the-controls-were-always-the-same-controls">The controls were always the same controls</h2>

<p>Here is the part that should reassure a finance person rather than threaten them. The controls finance built over a century are not accounting techniques. They are ideas about power, and IT has been running the same ideas under different names for decades.</p>

<ul>
  <li><strong>Segregation of duties</strong> is <em>least privilege</em>: nobody holds both ends of a transaction, and nobody holds a key they don’t need for the work.</li>
  <li><strong>The approval signature</strong> is the <em>change ticket</em>: a change to production requires a second person to say yes, in writing, before it happens.</li>
  <li><strong>The reconciliation</strong> is the <em>access review</em>: on a schedule, compare who should have what against who actually does, and explain every difference.</li>
  <li><strong>The audit trail</strong> is the <em>log</em>: an append-only record of who did what, when, that nobody can quietly edit afterward.</li>
</ul>

<p>The shell got there early. <code class="language-plaintext highlighter-rouge">sudo</code> exists so that the person who <em>can</em> do anything must say so, on the record, before the system will let them. That is a control a controller would recognize on sight. And the newest version of the same idea is that an instruction given to a model is an entry in a book of record, as <a href="/posts/2026/07/06/ai-audit-trail-log-prompts-like-journal-entries/" class="wiki-link" data-wiki-target="The AI audit trail: log prompts like journal entries">The AI audit trail: log prompts like journal entries</a> argues, and deserves the same discipline as a debit and a credit.</p>

<p>None of this needs to be invented. It needs to be <em>recognized</em> — by the finance side as their own controls wearing a hoodie, and by the IT side as the reason the controls exist.</p>

<h2 id="what-finance-has-that-it-still-doesnt">What finance has that IT still doesn’t</h2>

<p>The honest version of this argument also names the gap, because it is wide. Finance in a small business has three things IT almost never has.</p>

<p><strong>A close.</strong> Finance has a ritual on a calendar: the books are reconciled, the exceptions are explained, and someone signs. IT has “when something breaks.” There is no monthly moment when the subscription list is reconciled against the card statements, the access list is reviewed against the org chart, one backup is actually restored, and someone signs their name to the result. The information exists. The ritual doesn’t.</p>

<p><strong>A language every manager speaks.</strong> Almost every department head can read a profit and loss (P&amp;L) statement, because the company taught them to and promotions depended on it. Almost none can read a permissions model, a data-flow diagram, or an AI usage report. So the department that can see the whole company reports its findings in a language the rest of the company doesn’t read, and the findings go nowhere.</p>

<p><strong>A seat.</strong> The CFO reports to the owner. IT in an SMB reports to whoever is angriest today, usually through the ticket queue of a managed service provider (MSP), and the MSP is paid to keep the lights on, not to tell the owner what the systems reveal about the business. Nobody at the table owns the view.</p>

<p>Finance got its seat because owners learned, expensively, that the alternative was not knowing. The same lesson is arriving for IT, and it arrives faster: a company that cannot see its own systems cannot see its own operation anymore, because the operation <em>is</em> the systems.</p>

<h2 id="run-it-like-the-department-it-is">Run it like the department it is</h2>

<p>If IT is the new finance department, the move is not to hire a chief information officer. The move is to borrow the structure finance already figured out, at SMB scale, in four steps.</p>

<ol>
  <li><strong>Give it a ledger.</strong> A chart of accounts for your technology: every system, who owns it, what data it holds, what it costs a month, and who can log in. One page. In our experience it takes a few days to a week to build the first time and an hour or two a month to keep true, and it is the single document every later decision — a migration, an audit, a cyber-insurance renewal, an AI rollout — turns out to need. <a href="/tools/business/it-foundations/" class="wiki-link" data-wiki-target="The small-business IT foundation">The small-business IT foundation</a> walks through what belongs on it.</li>
  <li><strong>Give it a close.</strong> Monthly or quarterly, on the calendar: reconcile subscriptions to card statements, review access against the org chart, restore one backup to prove it restores, read the AI usage report, sign. An afternoon. The point is not the checklist; it’s that the company now has a moment when the systems are <em>looked at</em> instead of merely running.</li>
  <li><strong>Give it controls finance would recognize.</strong> An approval before a new subscription, not after. Least privilege by default, with the exceptions written down. A log for anything a model or an agent does in the company’s name. If you already run a real record-to-report process, most of this is the same discipline applied one layer down — <a href="/tools/partners/record-to-report/" class="wiki-link" data-wiki-target="Record-to-report automation and controls">Record-to-report automation and controls</a> shows what that layer looks like.</li>
  <li><strong>Give it a seat.</strong> Copy the structure finance uses at your size: an in-house owner, even part-time, who can speak both languages; an outside firm for the mechanical work, the way an outside bookkeeper handles the mechanical side of accounting; and a fractional strategist for the decisions, the way a fractional CFO sits in on the ones that matter. The MSP is your bookkeeper. It is not your CFO, and it was never going to be.</li>
</ol>

<p>The company that does these four things hasn’t reorganized. It has admitted something that was already true: the function that can see the whole operation is the one that runs the systems, and it deserves what finance already has: a ledger, a close, and a seat.</p>

<h2 id="next-step">Next step</h2>

<p>Finance took a century to go from a clerk with a ledger to a seat at the table. IT doesn’t have that long, because the spend, the record, and the risk have already moved. If you’d like the one-page ledger built for your own company — and a second reader who speaks both finance and IT to walk the first close with you — <a href="/contact/">tell us what you run</a>, and we’ll start with what’s actually on the card statements.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="muses" /><category term="strategy" /><category term="smb" /><category term="internal-controls" /><category term="segregation-of-duties" /><category term="shadow-it" /><category term="accounting" /><category term="denver-smb" /><summary type="html"><![CDATA[Software, data, and AI spend now move through a small business the way cash does, and the IT function metering them needs a ledger, a close, and controls]]></summary></entry><entry><title type="html">Your business runs on relics, and their makers have left</title><link href="https://www.bash-365.com/posts/2026/09/14/your-business-runs-on-relics-and-their-makers-have-left/" rel="alternate" type="text/html" title="Your business runs on relics, and their makers have left" /><published>2026-09-14T10:00:00+00:00</published><updated>2026-09-14T10:00:00+00:00</updated><id>https://www.bash-365.com/posts/2026/09/14/your-business-runs-on-relics-and-their-makers-have-left</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/09/14/your-business-runs-on-relics-and-their-makers-have-left/"><![CDATA[<h2 id="the-oldest-thing-in-the-building">The oldest thing in the building</h2>

<p>The oldest thing in your building is not the boiler, the founder’s desk, or the fax machine nobody has unplugged. It is the numbering scheme in your chart of accounts. Somebody designed it — a controller, probably, in a year when the company had one location and no idea it would ever need a second — and it has quietly governed every invoice, every close, and every report since. That controller retired. The numbering scheme did not.</p>

<p>The claim underneath all of it: an information system stops being technology on the day it goes live, and starts being an artifact. Technology is the word for what is coming. Artifact is the word for what was made, by particular people, for particular reasons, and then left behind. The distinction matters because we manage the two differently. Technology gets a roadmap. Artifacts get an archaeologist — or, more often, they get nothing, until the day someone has to change one and discovers that nobody still on payroll can say why it is the way it is.</p>

<h2 id="dig-anywhere-and-you-hit-a-decade">Dig anywhere and you hit a decade</h2>

<p>Every running system is a site with layers, and the layers are dated. A distributor on the Front Range that has been in business since the nineties is typically running something like this, from the bottom up:</p>

<ul>
  <li><strong>The 1990s stratum.</strong> The chart of accounts and the item master, designed by a person who has since retired, encoding decisions (“warehouse is segment three, always”) that everyone obeys and nobody can explain.</li>
  <li><strong>The mid-2000s stratum.</strong> Customizations to the Enterprise Resource Planning (ERP) system, written by a consultant whose firm no longer exists, in a language the vendor has since stopped teaching.</li>
  <li><strong>The 2010s stratum.</strong> The workarounds: a pricing workbook with a macro in it, a nightly script that moves a file between two systems that were never meant to meet, a report that everyone trusts and nobody has rerun by hand since it was built.</li>
  <li><strong>The 2020s stratum.</strong> A web front end, a cloud backup, a dashboard, and this year an artificial intelligence (AI) pilot pointed at all of it.</li>
</ul>

<p>Each layer was rational in its own year. That is what makes the site hard to read: the code records what was done, and the reason went home with whoever did it. An archaeologist can tell you a wall was built; they have to infer why. Your information technology (IT) lead is in the same position with the nightly script that runs at 2 a.m. It runs at 2 a.m. because the tape backup used to finish at 1:30. The tape drive left the building a decade ago. The timing stayed, a ritual whose god has retired.</p>

<p>If that sounds like a small-business problem, it is worth knowing that the largest organizations on earth have the same problem at a larger scale. In July 2025 the Government Accountability Office (GAO) examined eleven critical federal systems and put them at <a href="https://www.gao.gov/products/gao-25-107795">about 23 to 60 years old</a>. Eight of the eleven still run on legacy languages such as Common Business-Oriented Language (COBOL) and assembly, “programming languages that have a dwindling number of people available with the skills needed to support them.” In the spring of 2020, the governor of New Jersey stood at a pandemic briefing and asked for volunteers who could program in COBOL, because the state’s unemployment system was more than forty years old and drowning in claims.</p>

<p>And when Voyager 1, launched in 1977, stopped sending readable data in late 2023, the National Aeronautics and Space Administration (NASA) noted that finding solutions to the probes’ problems <a href="https://science.nasa.gov/blogs/voyager/2023/12/12/engineers-working-to-resolve-issue-with-voyager-1-computer-2/">“often entails consulting original, decades-old documents written by engineers who didn’t anticipate the issues that are arising today”</a>. Engineers <a href="https://www.jpl.nasa.gov/news/nasas-voyager-1-resumes-sending-engineering-updates-to-earth/">fixed it the following spring by dividing the affected code into sections and storing them elsewhere in the computer’s memory</a>, around a chip that had failed. The fix was new. The knowledge required to make it safely was older than most of the careers in the room.</p>

<p>Your business is not a space program. The mechanism is the same at every scale; the scale only changes who the elders are. At NASA the elders wrote the documents. At a forty-person machine shop, the elder is the controller who built the pricing workbook, the administrator who wrote the backup script, and the consultant from the Mfg/Pro days who still answers email, for now.</p>

<h2 id="the-artifact-survives-the-understanding-doesnt">The artifact survives, the understanding doesn’t</h2>

<p>A little more than two thousand years ago, someone in the Greek world built a bronze box of interlocking gears that modeled the heavens: it tracked the moon’s phases and predicted eclipses, and the researchers who decoded it call it <a href="https://www.nature.com/articles/nature05357">“technically more complex than any known device for at least a millennium afterwards”</a>. It went down with a ship, spent two millennia on the seabed, and came up in 1901 as a corroded lump that took another century to read.</p>

<p>Notice what survived and what did not. The object survived. The understanding — how to design it, why those gear ratios, what the maker knew that let them build it at all — did not survive on land, where there was no shipwreck to blame. Nobody destroyed that knowledge. It lived in a few heads, was never written where the next person could find it, and went when they went.</p>

<p>That is the condition of most business systems. They are fully legible as objects and nearly illegible as intentions. The code runs; the <em>why</em> is gone. And there is a particular cruelty in the timing: a system works best right before the last person who understands it leaves, because that person has spent years tuning it. Retirement does not break the system. It breaks the ability to change the system, and change is precisely what the new project requires.</p>

<h2 id="why-the-newest-project-needs-the-oldest-knowledge">Why the newest project needs the oldest knowledge</h2>

<p>Look at what is actually on the roadmap of a small or medium business (SMB) this year: a move off an unsupported ERP version, a cloud migration, an integration between the shop floor and the ledger, an AI assistant that can answer questions about the company’s own data. Every one of those is gated by the oldest knowledge in the company.</p>

<p>You cannot migrate what you cannot read. The ERP move needs someone who knows why the account string has fourteen segments and which nine are dead. The integration needs someone who knows that status code 7 means “shipped but disputed,” a decision made in 2006 and recorded nowhere. The AI assistant is the sharpest case, because a model is a reader, and it inherits every undocumented convention in what it reads. It can see the table. It cannot know what the warehouse manager meant. Pointed at an undocumented system, an AI produces confident archaeology: fluent, plausible stories about what each layer means, unchecked against the one person who could check them. The output looks like understanding and is a guess with good grammar.</p>

<p>There is a second, more hopeful sense in which new solutions require ancient wisdom. The techniques underneath your systems are old because they are right, not because nobody got around to replacing them. Double-entry bookkeeping is the oldest business information system still in production; Luca Pacioli described it in print in 1494, and every ERP ever sold is that Venetian ledger with a login screen. The relational model that organizes nearly every database you own was set out in a <a href="https://dl.acm.org/doi/10.1145/362384.362685">1970 paper by Edgar Codd</a>; its author died in 2003 and its ideas have not aged a day. The habits that make cloud infrastructure manageable — small tools that do one thing, plain text, a script instead of a memory — were worked out on machines with less memory than your thermostat. The people are gone. The ideas hold.</p>

<p>So “old” is not the problem. “Unread” is. A modernization that discards the old <em>idea</em> along with the old <em>housing</em> rebuilds, at cloud prices, the mistakes the old idea already solved. The value of the retiree is not nostalgia. It is that they carry the reasons, and the reasons are what tell you which layers to keep.</p>

<h2 id="the-rosetta-stone-is-a-reconciliation">The Rosetta Stone is a reconciliation</h2>

<p>Scholars could read Egyptian hieroglyphs only after the Rosetta Stone gave them the same decree in three scripts and two languages — a bilingual artifact that let a known language unlock an unknown one. Systems work has an exact equivalent, and it is less glamorous than a museum piece: the reconciliation.</p>

<p>A trial balance that ties out in both the old system and the new. An accounts receivable (AR) aging that matches to the penny across the cutover. An inventory valuation that agrees between the green screen and the browser. Each is the same decree carved in two scripts, and it is the only proof available that you understood the old system rather than merely copied it. A manufacturer that moved <a href="/case-studies/qad-mfgpro-upgrade/" class="wiki-link" data-wiki-target="From Mfg/Pro to QAD Enterprise Edition without losing data">From Mfg/Pro to QAD Enterprise Edition without losing data</a> rehearsed the migration with reconciliations back to the old system before anyone set a cutover date, and go-live was an anticlimax, which is the entire goal. A parallel run is a translation exercise. A migration without a defined reconciliation is a translation nobody checked.</p>

<p>The test works in reverse, too. If you cannot produce a bilingual artifact for a system — if there is no number the old and the new can both be asked for — you have not finished reading it, and the new system will carry the old one’s misreadings forward under a fresh coat of paint.</p>

<h2 id="what-to-do-before-the-next-retirement-party">What to do before the next retirement party</h2>

<p>Archaeology is a practice, not a mood, and the practical version fits a small business. Here are five moves, roughly in order; <a href="/tools/business/legacy-system-revival/" class="wiki-link" data-wiki-target="Reviving a legacy system without losing what it knows">Reviving a legacy system without losing what it knows</a> walks each one through in full, with the four questions to put to any vendor who proposes the work.</p>

<ol>
  <li><strong>Map the strata.</strong> One page per system: its age, who built each layer, who can still read it, and what depends on it. The “who can still read it” column is your real risk register. Wherever the answer is one name, and that name is within a few years of retirement or a few months of boredom, that row is a project, whatever the roadmap says. For what the map looks like when it has six interfaces from four decades on it, see <a href="/news/erp/frankenstein-erp-legacy-fragmentation/">Frankenstein’s ERP</a>.</li>
  <li><strong>Interview the elders while they are still on payroll.</strong> Two or three recorded afternoons with the person who built the thing. Ask for the <em>why</em>, not the <em>how</em>; the how is in the artifact. What would break if this ran an hour later? Which fields are lies? What did you decide not to build, and why? Write the answers in plain text and store them next to the system’s configuration, under version control. This costs days and is irreplaceable the week after they leave. It is also a courtesy: it says their judgment mattered, which it did.</li>
  <li><strong>Carve the bilingual artifact before you migrate.</strong> For every system you intend to replace, define the reconciliation up front: which numbers must agree, to what tolerance, on which dates. Then run it in rehearsal, more than once, and treat the first clean tie-out as the real milestone.</li>
  <li><strong>Keep the idea, replace the housing.</strong> Interrogate each layer with one question: is this old because it is right, or old because nobody has looked? Double-entry, normalized data, and a nightly reconciliation are the first kind. The 2 a.m. timing and the fourteen-segment account string are the second. Modernize the second on purpose and carry the first forward on purpose; how to stand a new system beside an old one without a big-bang cutover, using application programming interfaces (APIs) and events, is the subject of <a href="/tools/partners/integration-architecture/" class="wiki-link" data-wiki-target="Integration architecture: APIs, events, legacy">Integration architecture: APIs, events, legacy</a>.</li>
  <li><strong>Write for the archaeologist.</strong> Everything you build this year is a future relic. Leave the <em>why</em> where the next reader will dig: a plain-text note beside the code, a changelog, acronyms expanded, decisions recorded with the alternatives you rejected. It is the discipline this practice runs itself on: <a href="/tools/partners/deterministic-first/" class="wiki-link" data-wiki-target="The deterministic-first doctrine">The deterministic-first doctrine</a> is a set of files, not a memory. The reason fits in one line — the next reader may be a new hire, a consultant, or a model, and all three can only read what was written down.</li>
</ol>

<h2 id="next-step">Next step</h2>

<p>The knowledge is still in the building. It is walking toward the parking lot one retirement at a time. If you have a system whose only reader is planning a farewell lunch, or a migration whose reconciliation nobody has defined yet, <a href="/contact/">tell us what is in the oldest layer</a>, and we’ll help you map the strata and carve the bilingual artifact while the people who can check it are still answering email.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="muses" /><category term="legacy-systems" /><category term="strategy" /><category term="systems-thinking" /><category term="change-management" /><category term="smb" /><category term="denver-smb" /><summary type="html"><![CDATA[Your legacy systems are artifacts of people who have retired, and the cloud, ERP, or AI project you want next depends on recovering what they knew]]></summary></entry><entry><title type="html">ERP customizations should be the default</title><link href="https://www.bash-365.com/posts/2026/09/13/erp-customizations-should-be-the-default/" rel="alternate" type="text/html" title="ERP customizations should be the default" /><published>2026-09-13T03:45:00+00:00</published><updated>2026-09-13T03:45:00+00:00</updated><id>https://www.bash-365.com/posts/2026/09/13/erp-customizations-should-be-the-default</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/09/13/erp-customizations-should-be-the-default/"><![CDATA[<p>For twenty years, “customization” was the word that made Enterprise Resource Planning (ERP) vendors clear their throats and implementation partners reach for the change-order form. The business wanted the system to match how work actually ran. The vendor wanted “fit to standard.” Both sides were defending a real interest. Both sides turned the word into a fight.</p>

<p>That fight made sense when every change meant hand-written code, a brittle upgrade path, and a specialist who might retire before the next release. It makes less sense now that AI agents and Grok Bots can draft, document, test, and re-apply adaptations at a fraction of the old cost. Customizations should not be treated like a confession. They should be the default — governed, owned, and expected.</p>

<h2 id="why-businesses-and-vendors-fought-over-the-word">Why businesses and vendors fought over the word</h2>

<p>The business case for customizing was never mysterious. Your warehouse receives against a process the vendor never modeled. Your project costing needs a dimension the template chart of accounts does not care about. Your month-end close depends on a spreadsheet that became load-bearing software while nobody was looking. Fitting the company to the package means changing people, controls, and sometimes customer promises. Fitting the package to the company means changing software.</p>

<p>Vendors pushed back for equally concrete reasons:</p>

<ul>
  <li><strong>Upgrade economics.</strong> Heavy custom code raised the cost and risk of moving to the next release, which is how vendors monetize the roadmap.</li>
  <li><strong>Support boundaries.</strong> “Modified” environments become “you own that defect” conversations.</li>
  <li><strong>Template sales.</strong> Standard processes are easier to demo, staff, and productize across hundreds of customers.</li>
</ul>

<p>So the negotiation settled into a ritual: the partner scores a fit-gap, the vendor sells “best practice,” the business sneaks in side systems, and five years later IT is maintaining Frankenstein while marketing slides still say “out of the box.”</p>

<h2 id="what-actually-changed-with-ai-agents">What actually changed with AI agents</h2>

<p>The old objection was not morality. It was <strong>total cost of ownership (TCO)</strong> on change. Writing the customization was expensive. Explaining it to the next hire was expensive. Rebuilding it after an upgrade was the bill that killed budgets.</p>

<p>AI agents compress those three costs:</p>

<ol>
  <li><strong>Drafting the adaptation.</strong> A Grok Bot or similar desktop agent can turn a recorded process, a sample file, and a clear acceptance test into a first-pass extension, script, or integration sketch — then iterate with the controller in the room.</li>
  <li><strong>Documentation as a byproduct.</strong> The same session that builds the change can leave an Architecture Decision Record (ADR), a runbook, and a test checklist. The customization stops living only in one developer’s head.</li>
  <li><strong>Re-application after upgrades.</strong> When the vendor ships a new release, agents can diff what broke, propose patches, and re-run the acceptance checks you already wrote. You still need a human merge gate. You no longer need a six-month archaeology project to remember why field X exists.</li>
</ol>

<p>None of that makes reckless customization free. It changes the default question from “how do we avoid customizing?” to “how do we customize so the next upgrade is boring?”</p>

<h2 id="embrace-does-not-mean-modify-the-core">Embrace does not mean “modify the core”</h2>

<p>“Customizations should be the default” is not permission to edit base objects with a crowbar. The durable pattern for small and medium businesses (SMBs) looks like this:</p>

<table>
  <thead>
    <tr>
      <th>Prefer</th>
      <th>Avoid</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Extensions, plugins, and documented APIs</td>
      <td>Direct edits to vendor base code</td>
    </tr>
    <tr>
      <td>Configuration and dimensions before code</td>
      <td>New general ledger (GL) accounts for every reporting whim</td>
    </tr>
    <tr>
      <td>Side-by-side services that integrate cleanly</td>
      <td>Shadow ERPs in Excel with no owner</td>
    </tr>
    <tr>
      <td>Acceptance tests the business can re-run</td>
      <td>“It works on the consultant’s laptop”</td>
    </tr>
    <tr>
      <td>Named owners and an ADR for each change</td>
      <td>Tribal knowledge and Slack archaeology</td>
    </tr>
  </tbody>
</table>

<p>AI makes the left column cheaper. It does not make the right column wise.</p>

<h2 id="how-it-plays-out-for-a-denver-smb">How it plays out for a Denver SMB</h2>

<p>For a typical 40–150 person manufacturer, distributor, or professional-services firm on Microsoft Dynamics 365 Business Central, NetSuite, or a similar mid-market ERP, a practical sequence is:</p>

<ol>
  <li><strong>Inventory the “unauthorized ERP.”</strong> List the spreadsheets, Access databases, and tribal procedures that already customize the business. That inventory is your real gap list.</li>
  <li><strong>Rank by cash and risk.</strong> Start with the adaptations that burn the most hours in the close, the warehouse, or revenue recognition — not the loudest opinion in the kickoff meeting.</li>
  <li><strong>Build with an agent, gate with a human.</strong> Use AI to draft the extension or integration; require a named business owner to accept tests; keep segregation of duties on anything that touches postings.</li>
  <li><strong>Treat each customization as a product.</strong> Version it, document why it exists, and schedule a revisit when the vendor’s roadmap overlaps.</li>
  <li><strong>Budget upgrades as re-apply, not rewrite.</strong> Hold a small reserve each year for agent-assisted revalidation instead of a panic project every major release.</li>
</ol>

<p>Timeline ranges vary, but standing up that governance habit is usually weeks of part-time work, not a multi-year program. The expensive part remains deciding what the business actually needs.</p>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>Agents invent confidently.</strong> Untested customizations are still liabilities. If a hack did not run, it is a note, not a deployment.</li>
  <li><strong>Vendor terms still matter.</strong> Some clouds restrict what you may extend. Read the contract before you celebrate.</li>
  <li><strong>Unowned AI output is yesterday’s side spreadsheet.</strong> If nobody is accountable for the customization, you rebuilt shadow IT with better autocomplete.</li>
  <li><strong>Fit-to-standard is still right for commodity processes.</strong> Payroll tax calculations and bank file formats rarely deserve a personal rewrite. Customize where your process is the product; standardize where the law already decided.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If your last ERP debate treated “customization” like a risk to be minimized instead of a capability to be governed, it is time to update the playbook. See our <a href="/services/erp/">ERP consulting</a> service for how we scope fit-gap work, extensions, and AI-assisted delivery for Denver SMBs — or <a href="/contact/">contact</a> us when you are ready to turn the old fight into an owned roadmap.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="erp" /><category term="ai" /><category term="erp" /><category term="customizations" /><category term="ai" /><category term="grok-bot" /><category term="governance" /><category term="upgrades" /><category term="vendor-lock-in" /><category term="smb" /><summary type="html"><![CDATA[ERP customizations used to mean upgrade pain and vendor fights. With AI and Grok Bots, they should be the default — with governance, not guilt.]]></summary></entry><entry><title type="html">How to roll out phishing-resistant MFA</title><link href="https://www.bash-365.com/posts/2026/09/10/how-to-roll-out-phishing-resistant-mfa/" rel="alternate" type="text/html" title="How to roll out phishing-resistant MFA" /><published>2026-09-10T12:00:00+00:00</published><updated>2026-09-10T12:00:00+00:00</updated><id>https://www.bash-365.com/posts/2026/09/10/how-to-roll-out-phishing-resistant-mfa</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/09/10/how-to-roll-out-phishing-resistant-mfa/"><![CDATA[<p>Your team enabled multi-factor authentication (MFA) last year, checked the box on the cyber-insurance form, and moved on. Then an employee’s phone lit up with twenty login approval prompts at 11 p.m. — not because anyone was trying to log in from home, but because an attacker already had the password and was betting someone would tap “approve” just to make the notifications stop.</p>

<h2 id="why-it-matters-now">Why it matters now</h2>

<p>That attack has a name — push bombing, or MFA fatigue — and it works often enough that it’s now a standard step in commodity phishing kits, not a nation-state trick. A second, quieter attack does the same thing without the noise: an adversary-in-the-middle phishing page that proxies your real login page in real time, captures the password, and relays the MFA code the instant the victim types it. Both attacks beat the exact control we told everyone to “just turn on” in <a href="/tools/business/security-baseline/" class="wiki-link" data-wiki-target="The security baseline every small business needs">The security baseline every small business needs</a>. Push notifications and one-time codes are real MFA and still stop plain password-stuffing, but they no longer count as the finish line — carriers and the U.S. Cybersecurity and Infrastructure Security Agency (CISA) now distinguish basic MFA from <a href="https://www.cisa.gov/resources-tools/resources/multi-factor-authentication-mfa">phishing-resistant MFA</a>, which uses cryptographic keys that a fake login page cannot relay.</p>

<h2 id="what-wed-actually-do">What we’d actually do</h2>

<p>Phishing-resistant MFA means the second factor is a hardware security key or a device-bound passkey built on the FIDO2/WebAuthn standard, instead of a code or a tap. The credential is tied to the real website’s domain, so it simply doesn’t work on a look-alike phishing page — there’s nothing for the attacker to steal or relay. Both Microsoft 365 (via Conditional Access authentication strengths) and Google Workspace (via 2-Step Verification security keys and passkeys) support this today, on hardware most staff already carry — a laptop’s built-in fingerprint reader or a $25–$50 Universal Serial Bus (USB) security key.</p>

<p>The honest scope: this is a configuration and change-management project, not a development project. For a business without on-premises legacy applications, expect days of admin work plus a few weeks of staff rollout, not months.</p>

<h2 id="how-it-plays-out">How it plays out</h2>

<ol>
  <li><strong>Audit current methods (week 1).</strong> Pull the MFA method report from your Microsoft 365 or Google Workspace admin console. You’re looking for how many users are still on SMS or voice-call codes — both are phishable and increasingly unreliable against SIM-swap fraud — versus app-based push.</li>
  <li><strong>Protect the accounts that matter most first (weeks 1–2).</strong> Enroll finance staff, anyone with administrator rights, and owners with a hardware security key or platform passkey. These are the accounts a targeted attacker goes after by name, so they move first regardless of company size.</li>
  <li><strong>Turn on number matching everywhere else (week 2).</strong> For staff not yet on a security key, enable number matching on push approvals — the app shows a number the user must type back, which kills the “just tap approve” version of push bombing even before hardware keys arrive.</li>
  <li><strong>Extend security keys or passkeys to everyone (weeks 3–6).</strong> Roll out by department. A professional-services firm can usually do this in one sitting per team; a construction company with field crews sharing tablets needs a slower pass and a clear policy on who owns which key.</li>
  <li><strong>Retire SMS and voice codes (after rollout).</strong> Once adoption is confirmed, disable phone-based MFA as a fallback option, not just as a default — a fallback an attacker can still request is a fallback that still gets used.</li>
</ol>

<h2 id="watch-outs">Watch-outs</h2>

<ul>
  <li><strong>Lost keys need a real process, not a shrug.</strong> Decide in advance how a lost security key gets revoked and replaced same-day, with a second, backed-up key or passkey as the recovery path — never a phone call to the help desk as the only fallback.</li>
  <li><strong>Shared and field devices break the “one passkey per person” model.</strong> Construction and light-manufacturing shops with shared shop-floor terminals should default those accounts to hardware security keys staff carry with them, not device-bound passkeys tied to a machine nobody owns.</li>
  <li><strong>Legacy line-of-business software is often the real blocker.</strong> An on-premises application that only accepts a password, or a VPN client that hasn’t been updated in years, can’t enforce modern MFA at all — that gap usually needs a compensating control or a <a href="/services/managed-it/" class="wiki-link" data-wiki-target="Managed IT services">Managed IT services</a> engagement to close properly, not a policy memo.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>If your MFA rollout stalled at “enabled” and you’re not sure which accounts are still one convincing phishing page away from a breach, an audit of your current authentication methods is a same-week engagement under <a href="/services/managed-it/" class="wiki-link" data-wiki-target="Managed IT services">Managed IT services</a>.</p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="tech" /><category term="mfa" /><category term="phishing" /><category term="security" /><category term="identity" /><category term="smb" /><summary type="html"><![CDATA[A phased plan for moving Microsoft 365 and Google Workspace logins from push-based MFA to phishing-resistant passkeys and security keys]]></summary></entry><entry><title type="html">ERP Survivor, episode 2: the great chart of accounts debate</title><link href="https://www.bash-365.com/posts/2026/09/03/erp-survivor-episode-2-the-great-chart-of-accounts/" rel="alternate" type="text/html" title="ERP Survivor, episode 2: the great chart of accounts debate" /><published>2026-09-03T12:00:00+00:00</published><updated>2026-09-03T12:00:00+00:00</updated><id>https://www.bash-365.com/posts/2026/09/03/erp-survivor-episode-2-the-great-chart-of-accounts</id><content type="html" xml:base="https://www.bash-365.com/posts/2026/09/03/erp-survivor-episode-2-the-great-chart-of-accounts/"><![CDATA[<h2 id="previously-on-erp-survivor">Previously on ERP Survivor</h2>

<p><em>Dramatic music. A single spreadsheet tab, still open, glowing in an otherwise dark office.</em></p>

<p><strong>Narrator</strong>: “Last season, the Alliance of the Spreadsheet People formed to resist the new system. The consultants recruited them instead of fighting them. Requirements gathering is done. Now the tribes face the challenge that ends more Enterprise Resource Planning (ERP) projects than any go-live disaster: designing the chart of accounts (COA), the numbered list of every account the general ledger (GL) will ever use.”</p>

<p>The alliance formed in <a href="/posts/2025/08/12/erp-survivor-episode-1/" class="wiki-link" data-wiki-target="ERP Survivor, episode 1: alliance of the spreadsheet people">ERP Survivor, episode 1: alliance of the spreadsheet people</a>. If you are the controller signing off on that list — or the information technology (IT) lead who will be asked to build reports on top of it — this is the episode for you. The lessons are in the commercial breaks, same as always.</p>

<h2 id="why-this-challenge-matters-now">Why this challenge matters now</h2>

<p>A chart of accounts looks like plumbing — dull, structural, easy to defer. It is actually the single decision that determines whether your month-end close is fast or painful for the next decade. A bloated COA, with a separate account for every project, customer, or department, turns every financial report into a scavenger hunt and every new hire into a six-month COA archaeologist. A COA that is too thin loses the detail management actually needs.</p>

<p>For a Denver light-manufacturing or distribution shop, this shows up as a very specific pain: finance wants to slice profit by project, operations wants to slice it by product line, and everyone’s instinct is “add more accounts.” Do that enough times and a chart designed for 200 accounts balloons to 2,000, nobody remembers what half of them are for, and closing the books means reconciling accounts nobody has posted to in three years.</p>

<h2 id="the-immunity-challenge-design-the-chart-of-accounts">The immunity challenge: design the chart of accounts</h2>

<p><strong>Host</strong>: “Today’s challenge: each tribe proposes a chart of accounts structure. The tribe whose structure survives contact with an actual audit wins immunity.”</p>

<h3 id="finance-tribes-opening-bid">Finance tribe’s opening bid</h3>

<p><strong>Linda</strong>: “We need one account per project. We track 47 active projects. That’s 47 new accounts, times however many expense categories, times two for this year and next.”</p>

<p><strong>Sarah</strong> <em>(already building the account-numbering spreadsheet)</em>: “So somewhere around six hundred new posting accounts, conservatively.”</p>

<p><strong>Dave</strong>: “And we keep the old chart too. Just in case.”</p>

<blockquote>
  <p><strong>Commercial break — the lesson:</strong> This is the classic mistake, and it comes from a real need: Linda’s team needs to report profit by project. The wrong fix is a GL account per project. The right fix is a <strong>dimension</strong> — a tag you attach to a transaction (project, department, location) that lets you slice reports without multiplying the chart itself. Microsoft’s own guidance on <a href="https://learn.microsoft.com/en-us/dynamics365/business-central/finance-chart-of-accounts">designing a chart of accounts for Dynamics 365 Business Central</a> puts it plainly: “Consider starting simple with fewer G/L accounts” and “Use dimensions to simplify your chart of accounts. Don’t have specific G/L accounts for each product or department.” One clean expense account with a project dimension answers Linda’s question and every other cut she will ever ask for, including the ones she has not thought of yet.</p>
</blockquote>

<h3 id="operations-tribe-counters">Operations tribe counters</h3>

<p><strong>Bob</strong>: “We don’t need 47 project accounts. We need our own inventory adjustment account, our own scrap account, and a way to fix mistakes without waiting for anyone’s approval.”</p>

<p><strong>Carol</strong>: “What do you mean, ‘waiting for approval’? Bob just posts the correction himself. He’s been doing it since the Reagan administration.”</p>

<p><strong>Mike</strong>: “It’s called the emergency override procedure. Bob has the login. Bob makes the fix. Bob moves on.”</p>

<p><strong>Bob</strong> <em>(proudly)</em>: “Twenty minutes, start to finish. No meetings.”</p>

<blockquote>
  <p><strong>Commercial break — the lesson:</strong> Bob’s “emergency override” is a segregation-of-duties gap, not a shortcut — the same person who identifies an error, approves the correction, and posts it, with nobody else reviewing it. It survives in a legacy system because nobody’s looked in years. It will not survive a lender’s or insurer’s first real audit, and it is exactly the kind of finding that turns a clean review into a management-letter comment. The fix is not to accuse Bob of anything; his corrections are probably right. The fix is a second set of eyes: a same-day review queue, or a threshold above which someone else approves before the entry posts. That takes Bob from twenty minutes to maybe forty-five, and it takes segregation of duties from “trust Bob” to “provable.”</p>
</blockquote>

<h3 id="it-tribe-tries-to-referee">IT tribe tries to referee</h3>

<p><strong>Jennifer</strong>: “What if we just gave both tribes everything they asked for? More accounts, more overrides, more flexibility.”</p>

<p><strong>Tom</strong> <em>(flipping through his binder)</em>: “The methodology recommends a standard chart of accounts with minimal customization, but it also has a whole appendix on flexibility, so—”</p>

<p><strong>Alex</strong>: “I could build a report that hides the accounts nobody uses. Problem solved.”</p>

<blockquote>
  <p><strong>Commercial break — the lesson:</strong> “Give everyone what they asked for” is not a design decision, it is the absence of one, and it is how a 200-account chart becomes a 2,000-account chart within eighteen months. Someone has to own the chart of accounts as a single artifact — usually the controller, with the implementation partner as a sounding board — and say no to additions that a dimension, a report filter, or a saved view can handle instead.</p>
</blockquote>

<h2 id="the-plot-twist-the-guest-judge-draws-two-lists">The plot twist: the guest judge draws two lists</h2>

<p><strong>Guest Judge</strong>: “Every account request goes on one of two lists today. List one: things that change the actual financial structure of the business — a new legal entity, a new revenue stream, a new tax jurisdiction. Those get new accounts. List two: things you want to slice, filter, or report on — a project, a customer, a sales rep, a department. Those get dimensions.”</p>

<p><strong>Linda</strong> <em>(reading the lists)</em>: “…my 47 projects are all on list two.”</p>

<p><strong>Guest Judge</strong>: “Every one. You’ll get project-level profitability without a single new account.”</p>

<p><strong>Bob</strong> <em>(quieter now)</em>: “And the override procedure?”</p>

<p><strong>Guest Judge</strong>: “Stays, with one change: a second approver on anything over a threshold you set. You keep the speed. The business gets the proof.”</p>

<blockquote>
  <p><strong>Commercial break — the lesson:</strong> Most chart-of-accounts fights are really reporting fights wearing a structural costume. Separating “this changes what the business is” from “this changes how I want to view what the business already does” resolves the majority of them before a single account number gets assigned.</p>
</blockquote>

<h2 id="how-a-real-chart-of-accounts-design-plays-out">How a real chart of accounts design plays out</h2>

<ol>
  <li><strong>Inventory the current chart and every report built on it (1-2 weeks).</strong> Include the accounts nobody remembers the purpose of — those are usually the first to retire.</li>
  <li><strong>Adopt a standard structure, then customize only where a real business rule demands it (1-2 weeks).</strong> Most mid-market ERP platforms ship a sensible default chart for your industry; start there instead of rebuilding from scratch.</li>
  <li><strong>Map every “I need to slice by X” request to a dimension, not an account (1-2 weeks).</strong> Project, department, location, and sales channel almost always belong here.</li>
  <li><strong>Design the control matrix alongside the chart, not after go-live (1-2 weeks).</strong> Decide who can post, who approves exceptions, and at what dollar threshold — including Bob’s overrides. Our <a href="/tools/partners/record-to-report/" class="wiki-link" data-wiki-target="Record-to-report automation and controls">Record-to-report automation and controls</a> guide lists the control points worth writing down.</li>
  <li><strong>Pilot the close with the new chart for one full cycle before retiring the old one.</strong> A chart that looks right on paper still has to survive an actual month-end.</li>
</ol>

<h2 id="watch-outs-that-eliminate-contestants">Watch-outs that eliminate contestants</h2>

<ul>
  <li><strong>Account-per-project sprawl.</strong> If a request can be answered with a dimension, it is not a new account. Every exception you allow becomes the precedent for the next one.</li>
  <li><strong>Unreviewed override authority.</strong> Anyone who can identify, approve, and post a correction alone is a finding waiting for an auditor to notice it. Fix the workflow before it fixes your quarter.</li>
  <li><strong>A chart designed by committee with no owner.</strong> Someone has to have the authority to say no to a new account. Without that, the chart grows until nobody trusts it.</li>
</ul>

<h2 id="next-step">Next step</h2>

<p>Redesigning your chart of accounts as part of a bigger system change? See our <a href="/services/erp/" class="wiki-link" data-wiki-target="ERP consulting">ERP consulting</a>, including the financial and control design work that keeps Linda’s reporting and Bob’s speed both intact.</p>

<hr />

<p><em>Based on patterns observed across dozens of ERP implementations. The characters are fictional. The chart of accounts disputes are not. No project codes were harmed in the making of this episode — 46 of them were, mercifully, retired in favor of a dimension.</em></p>

<p><em>Next episode: “User acceptance testing tribal council,” where Mike discovers what thirty years of undocumented data actually converts into, and the alliance faces its first vote.</em></p>]]></content><author><name>Amr Abdel-Motaleb</name></author><category term="erp" /><category term="erp-implementation" /><category term="change-management" /><category term="chart-of-accounts" /><category term="segregation-of-duties" /><category term="denver-smb" /><summary type="html"><![CDATA[How a chart of accounts debate teaches Denver controllers to design a general ledger that survives an audit, not just a spreadsheet]]></summary></entry></feed>