<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Douglas Rosenberg</title>
    <description>The latest articles on DEV Community by Douglas Rosenberg (@shalant).</description>
    <link>https://dev.to/shalant</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4146278%2F6bae2719-96e2-4282-9c34-21ba6c71329a.jpg</url>
      <title>DEV Community: Douglas Rosenberg</title>
      <link>https://dev.to/shalant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shalant"/>
    <language>en</language>
    <item>
      <title>One App, Two Audiences: Building a Customer Intake Backend with a Personal ERP Inside It</title>
      <dc:creator>Douglas Rosenberg</dc:creator>
      <pubDate>Mon, 12 Oct 2026 01:15:45 +0000</pubDate>
      <link>https://dev.to/shalant/one-app-two-audiences-building-a-customer-intake-backend-with-a-personal-erp-inside-it-55kb</link>
      <guid>https://dev.to/shalant/one-app-two-audiences-building-a-customer-intake-backend-with-a-personal-erp-inside-it-55kb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update (2026-10-11):&lt;/strong&gt; the two pieces this post lists as still ahead are done. The app has been live at &lt;code&gt;admin.dougrosenbergdev.com&lt;/code&gt; since 2026-09-25, and a GitHub Actions CI/CD pipeline (build and test on every push, a manually triggered deploy using OIDC federated login with no stored client secret) was confirmed working that same day. The sections below are the original text. The client portal is still a stub, and the MCP server is still a plan, not a build.&lt;/p&gt;

&lt;p&gt;My newest project isn't for a client — it's for me. &lt;code&gt;customer-intake-backend&lt;/code&gt; is a small .NET app that does two jobs: it's the public lead-intake system for two different businesses (my dev-consulting work and music-booking inquiries from the bands I play in), and it's also home to a private personal ERP I use to track my own freelance pipeline. Those two things share a process and a repo. They do not share a data boundary — and how I enforced that, not just declared it, is most of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Leads Table, Two Businesses
&lt;/h2&gt;

&lt;p&gt;The public-facing half is straightforward: a shared &lt;code&gt;Leads&lt;/code&gt; table tagged by &lt;code&gt;Source&lt;/code&gt;, so a booking inquiry from the band's site and a dev-services inquiry from this one land in the same schema instead of two one-off tables that happen to look identical. Both dougrosenbergmusic and Guacamayo's site are static (Astro on Cloudflare Workers) and can't run C#, so this exists as its own deployable service they call into over HTTP — &lt;code&gt;POST /api/leads&lt;/code&gt;, CORS-restricted to the sites that are allowed to post to it, rate-limited, with the unauthenticated &lt;code&gt;GET&lt;/code&gt; locked to development only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard Problem #1: Keeping a Private ERP Inside a Public-Facing App
&lt;/h2&gt;

&lt;p&gt;The personal ERP needed somewhere to live, and the obvious-sounding answer — a fully separate app — turned out to be the wrong default once I actually weighed it. A second repo means a second deployment, second auth, second CI/CD, all for a single-user internal tool. What the sensitivity of the data (real client pricing, an active pipeline) actually requires isn't process isolation, it's &lt;em&gt;data&lt;/em&gt; isolation. So the personal ERP lives in the same repo, same deployment, same login — but on its own &lt;code&gt;DbContext&lt;/code&gt; and its own database, with nothing shared at the EF Core layer.&lt;/p&gt;

&lt;p&gt;Declaring a boundary and enforcing one are different things, so I didn't stop at the design doc. There's an architecture test (&lt;code&gt;NetArchTest.Rules&lt;/code&gt;) that fails the build if the public-facing &lt;code&gt;Components.Pages&lt;/code&gt; namespace ever references the personal ERP's types, or if the personal ERP ever reaches for the public &lt;code&gt;CustomerIntakeDbContext&lt;/code&gt;. And I didn't just trust that the test worked — I deliberately introduced a violation, watched the build fail, then reverted it. If I ever copy-paste the wrong query, or an AI-assisted edit reaches for the wrong context, the build catches it before it ships. That's the realistic failure mode I was actually guarding against — not an attacker, just an ordinary solo-dev mistake at 11pm.&lt;/p&gt;

&lt;p&gt;The honest gap: none of this protects against the process itself being compromised — a dependency exploit gets an attacker both connection strings regardless of any C#-level type rule. That's accepted, not overlooked, and the upgrade path if the risk profile ever changes is narrowing network reachability, not re-architecting into a separate app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard Problem #2: A Status Field Can't Tell You When
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgdpqq5b7d47wb3iicsjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgdpqq5b7d47wb3iicsjd.png" alt="Before: a single overwritten Status field. After: dated LeadEvent rows, with CurrentStatus derived from the latest one" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original &lt;code&gt;Leads&lt;/code&gt; table tracks pipeline state with a single mutable &lt;code&gt;Status&lt;/code&gt; enum — &lt;code&gt;New&lt;/code&gt;, &lt;code&gt;Contacted&lt;/code&gt;, &lt;code&gt;Converted&lt;/code&gt;, &lt;code&gt;Closed&lt;/code&gt;. Every change overwrites the last one. There's no record of when a lead actually moved from &lt;code&gt;New&lt;/code&gt; to &lt;code&gt;Contacted&lt;/code&gt;, or what happened in between. Fine for a low-volume, single-admin intake flow — but designing the personal ERP's client pipeline from scratch, I didn't have to inherit that limitation.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;ClientLead.CurrentStatus&lt;/code&gt; is a cached projection, not the source of truth. The real source of truth is &lt;code&gt;LeadEvent&lt;/code&gt;: one row per thing that actually happened — outreach sent, a gig played, a proposal sent — each with its own date. Current status gets derived from the latest event instead of written to directly. The payoff is that questions like "how long between outreach and conversion" are answerable for free, straight from the schema, with no extra tracking bolted on. The cost is real too: every status change now means writing an event row instead of setting a field, and if something ever writes to &lt;code&gt;CurrentStatus&lt;/code&gt; directly instead of going through the event-append path, it can drift from the log. That's a discipline I'm holding by hand for now, not something the database enforces yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard Problem #3: Azure Assumed My Zip File Was Source Code
&lt;/h2&gt;

&lt;p&gt;Deployment was the least interesting problem to design and the most annoying to actually execute. The plan was ordinary — Azure App Service plus Azure SQL, reusing an existing free-tier plan for near-zero incremental cost. Getting there wasn't ordinary. The CLI path (&lt;code&gt;az webapp deploy&lt;/code&gt;, both locally and from Cloud Shell) kept invoking a server-side build step that failed with &lt;em&gt;"Couldn't detect a version for the platform 'dotnet'"&lt;/em&gt; — because the zip I was uploading was already-published output (compiled DLLs), not source, and the build tool had nothing buildable left to detect. It wasn't looking for what I'd actually given it.&lt;/p&gt;

&lt;p&gt;What worked was going around that path entirely: Visual Studio's built-in Publish, targeting the existing App Service over Web Deploy, which hits the classic deployment endpoint directly and skips the server-side build step altogether. Not the CI/CD story I eventually want (that's still explicitly deferred), but it got real code onto a real server without fighting a build system that was solving a problem I didn't have.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Backend: ASP.NET Core 10 minimal API + Blazor Server, one process
Data: EF Core 10 + Azure SQL (two separate databases: intake + personal ERP)
UI: MudBlazor, restyled to this site's navy/teal/Montserrat tokens
Auth: ASP.NET Core Identity, Admin/Client roles
Testing: unit tests on the service layer (SQLite in-memory), architecture tests enforcing the ERP boundary at build time
Hosting: Azure App Service, Web Deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's Actually Live
&lt;/h2&gt;

&lt;p&gt;As of this post: real auth is in place (Admin and Client roles, though the client-facing &lt;code&gt;/portal&lt;/code&gt; is currently a stub, not wired to real data). Leads and Companies have full CRUD with notes and an event log. The personal ERP's client-pipeline tracking is live with its own boundary-tested database. The whole thing is deployed and reachable — the custom subdomain and a proper CI/CD pipeline are the two pieces still ahead of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next (and Not Built Yet)
&lt;/h2&gt;

&lt;p&gt;I've been sketching a custom MCP server on top of the personal ERP's data — &lt;code&gt;list_leads&lt;/code&gt;, &lt;code&gt;get_lead&lt;/code&gt;, &lt;code&gt;add_event&lt;/code&gt;-shaped tools sitting on a domain model that's already close to tool-shaped. Microsoft co-maintains an official MCP C# SDK, so it stays in the same skill-building lane as the rest of this project rather than being a detour into a different stack. But I want to be straight about where it actually stands: it's a plan, not a build. It's sequenced deliberately after auth and deployment — both now done — specifically so there's something real and access-controlled to attach it to before any MCP client can reach personal data over the network. When it's built, it gets its own post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;None of this was hard in the "clever algorithm" sense. It was hard in the more common way real projects are hard: deciding where a boundary actually belongs, then proving — not just declaring — that it holds; picking a data shape that survives a question you haven't asked yet; and occasionally getting stopped cold by a deploy tool that assumed something about your inputs that wasn't true. If you're building something similar — a side project sharing infrastructure with something more sensitive — the architecture test is the part I'd steal first. A comment saying "don't reach across this boundary" survives until someone forgets. A build that fails does not.&lt;/p&gt;

&lt;p&gt;I build custom business software and ERP-style systems for small organizations, and I'm based in the Chicago area. If that's what you need, the details are on my &lt;a href="https://dougrosenbergdev.com/consulting/" rel="noopener noreferrer"&gt;consulting page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>blazor</category>
      <category>azure</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Rebuilding My Own Musician Site: From a Blazor Template to a Verified Astro Build</title>
      <dc:creator>Douglas Rosenberg</dc:creator>
      <pubDate>Sun, 11 Oct 2026 03:57:03 +0000</pubDate>
      <link>https://dev.to/shalant/rebuilding-my-own-musician-site-from-a-blazor-template-to-a-verified-astro-build-40an</link>
      <guid>https://dev.to/shalant/rebuilding-my-own-musician-site-from-a-blazor-template-to-a-verified-astro-build-40an</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxjihc03d0ggdgk4k2su.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxjihc03d0ggdgk4k2su.jpg" alt="The old Blazor-template version of dougrosenberg.com, before the rebuild" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Above: dougrosenberg.com's old Blazor-template site, archived here for reference before the rebuild replaced it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (2026-10-10):&lt;/strong&gt; the rebuilt site went live at &lt;a href="https://www.dougrosenberg.com" rel="noopener noreferrer"&gt;dougrosenberg.com&lt;/a&gt; the evening this was first published, and the Cloudflare Worker-backed contact form shipped that night. The "What's Still Open" section below is the original text; only the missing sheet music is still open.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Starting Point
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dougrosenbergdev.com/webdesign/dougrosenberg-music/" rel="noopener noreferrer"&gt;dougrosenberg.com&lt;/a&gt; is my other site — under a different hat, I'm a saxophonist, composer, and educator, and that's where that identity lives online. It's been running on Blazor WebAssembly on top of a stock component template: the same rounded glassmorphism cards, the same corner-blur decoration, the same floating circular chat launcher you'll find on a thousand other template-based sites. It worked. It just didn't read as built for anyone in particular — real content (a working musician's bio, performance history, a full sheet music library) sitting inside a shell that could have belonged to anyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Changed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dougrosenbergdev.com/webdesign/dougrosenberg-music-v2/" rel="noopener noreferrer"&gt;The rebuild&lt;/a&gt; (Astro, deployed to Cloudflare Workers) keeps every piece of real content — bio, performance credentials, albums, teaching history, sheet music, photo gallery, contact — and gives it an identity built around the one thing that's actually distinctive about the site: notation. A fixed nav rendered as icon-marked notes sitting on staff lines, a full-bleed broadcast-graded hero photo instead of a generic template hero, and a self-hosted type system (a display serif, a body sans, a monospace for the nav) instead of a default Google Fonts stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Migration
&lt;/h2&gt;

&lt;p&gt;Blazor WebAssembly ships a full C# runtime to the browser for what is, in the end, a static content site — the same structural mismatch I wrote about auditing on &lt;a href="https://dougrosenbergdev.com/blog/blazor-wasm-geo-audit/" rel="noopener noreferrer"&gt;my portfolio site&lt;/a&gt;. Astro prerenders to static HTML served straight from Cloudflare's edge instead, and that switch is what made the rest of the work verifiable instead of assumed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Lighthouse CI run against the production build on every pull request — 90+ performance/best-practices/SEO, 95+ accessibility, measured, not estimated&lt;/li&gt;
&lt;li&gt;  A 36-test Playwright end-to-end suite covering navigation, gallery, and search behavior, run against a real browser at real viewport widths on every PR&lt;/li&gt;
&lt;li&gt;  A real Content-Security-Policy with sha256-pinned inline scripts, HSTS, and the rest of the security-header baseline, checkable in the response headers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Concrete Example: Verifying an Optimization Actually Works
&lt;/h2&gt;

&lt;p&gt;The sheet music library alone was 88MB of scanned PDFs and photos. The obvious move is “just recompress everything” — and it's the wrong move, because sheet music that's actually illegible defeats the point of the feature. Instead of a bulk pass, I wrote a script that recompresses a candidate file, re-renders it, and pixel-diffs the result against the original — it only keeps the change if the render comes back near-identical. Most of the payload turned out to already be JPEG-compressed inside its PDFs, so there was nothing safe left to win without heavier tooling. But the same script caught something real: one file had two images stacked at the exact same position on the page, which looked like a redundant duplicate. Deleting the “duplicate” actually changed what rendered, and the verification step correctly rejected that change and left the file untouched. Three other files did get real, verified reductions — one JPEG dropped 44.5% with no visible difference. The tool earned its keep by refusing bad changes as much as by accepting good ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Still Open
&lt;/h2&gt;

&lt;p&gt;Being honest about where this actually stands: the sheet music library is still catching up to what's live on the old site — seven pieces are missing, pending source files. Contact is currently a plain &lt;code&gt;mailto:&lt;/code&gt; link rather than the Cloudflare Worker-backed form I've built elsewhere. And the domain itself hasn't moved — the new build is live only on its Cloudflare Workers subdomain, since pointing the real domain at it is a real production step I'm not taking without deliberately deciding to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;A template gets you to “functional” quickly, and that's a legitimate reason to start there. But functional and distinctive are different bars, and closing that gap took more than a redesign — it took a foundation that could actually be checked: real performance numbers instead of assumed ones, a real test suite instead of “looks fine,” and a verification step even on something as unglamorous as image compression. If you're wondering whether your own site's template is quietly costing you, that's the actual question: not just how it looks, but whether you can prove what it does.&lt;/p&gt;

&lt;p&gt;I build custom websites for working musicians and bands, and I play in several Chicago bands myself. If that's what you're after, the details are on my &lt;a href="https://dougrosenbergdev.com/musician-websites/" rel="noopener noreferrer"&gt;musician websites page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>astro</category>
      <category>cloudflare</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Enterprise Engineering Discipline, Small-Business Budget: How I Approach SMB Web Development</title>
      <dc:creator>Douglas Rosenberg</dc:creator>
      <pubDate>Mon, 28 Sep 2026 12:44:19 +0000</pubDate>
      <link>https://dev.to/shalant/enterprise-engineering-discipline-small-business-budget-how-i-approach-smb-web-development-d4n</link>
      <guid>https://dev.to/shalant/enterprise-engineering-discipline-small-business-budget-how-i-approach-smb-web-development-d4n</guid>
      <description>&lt;p&gt;If you're a small business owner comparing a website builder against a five-figure agency quote, there's an option in between that doesn't get talked about enough: hire someone who's spent years building software for organizations that genuinely can't afford to get it wrong, then apply that same discipline at small-business scale and price. That's specifically what I do. This isn't a pitch — it's the honest case, backed by real projects rather than adjectives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Worlds, One Set of Habits
&lt;/h2&gt;

&lt;p&gt;By day, I build &lt;a href="https://dougrosenbergdev.com/webdesign/arborkin" rel="noopener noreferrer"&gt;full-stack systems&lt;/a&gt; for organizations where mistakes have real consequences: a &lt;a href="https://dougrosenbergdev.com/" rel="noopener noreferrer"&gt;secure data-masking pipeline&lt;/a&gt; and 50 custom reports relied on by 500+ staff at a global nonprofit's ERP system, a Fortune 10 client's document-processing platform, a manufacturing ERP rebuilt from the ground up. Off the clock, I'm a saxophonist and composer who plays in several working Chicago bands.&lt;/p&gt;

&lt;p&gt;Those two worlds punish sloppiness in the same shape, just at different stakes: a report that's wrong at 2am for 500 staff, or a wrong chart in front of a paying crowd on stage. Both teach the same habit — build it right the first time, verify it, don't ship a guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Enterprise Discipline" Actually Means at Small-Business Scale
&lt;/h2&gt;

&lt;p&gt;This isn't about over-engineering a five-page marketing site. It's specific, checkable things: security modeled correctly even on a $1,500 project (the same thinking behind that ERP data-masking pipeline, scaled down), performance budgets verified with real tooling instead of eyeballed — I wrote up &lt;a href="https://dougrosenbergdev.com/blog/rebuilding-musician-site-blazor-to-astro" rel="noopener noreferrer"&gt;a concrete example&lt;/a&gt; of a script that pixel-diffs image compression before accepting any change, rather than trusting a bulk pass — and accessibility built in at a WCAG 2.1 AA baseline, not bolted on after a complaint.&lt;/p&gt;

&lt;p&gt;This is the whole thesis behind &lt;a href="https://haxbyte.com" rel="noopener noreferrer"&gt;Haxbyte&lt;/a&gt;, the studio this work is delivered under: small business owners don't need enterprise complexity, but they shouldn't have to settle for generic, slow, or fragile sites either. Haxbyte documents a four-step build process — scope it in writing, build the thin slice, load it like it's real, ship then write it up — and every SMB project follows it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof, Not Promises
&lt;/h2&gt;

&lt;p&gt;Three real projects, not hypotheticals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://dougrosenbergdev.com/webdesign/sonus-construction" rel="noopener noreferrer"&gt;Sonus Construction Group&lt;/a&gt;&lt;/strong&gt; — a general contractor whose real audience is investors and developers doing diligence, not homeowners browsing a portfolio. The site is built as an editorial one-pager instead of a generic contractor template, because that's what actually earns trust with that audience.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://dougrosenbergdev.com/webdesign/hardware-etc" rel="noopener noreferrer"&gt;Hardware Etc&lt;/a&gt;&lt;/strong&gt; — as of December 2021, this was a single unstyled placeholder page (the &lt;a href="https://web.archive.org/web/20211206204438/http://hardwareetc.net/" rel="noopener noreferrer"&gt;archived version&lt;/a&gt; is still up). It's now built for two real audiences — spec-writers doing desktop research and field crews checking specs on a phone mid-install — with a private-label catalog organized for high-volume procurement browsing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://dougrosenbergdev.com/webdesign/arborkin" rel="noopener noreferrer"&gt;ArborKin&lt;/a&gt;&lt;/strong&gt; — not client work. A private, invite-only family tree app I built and shipped end to end, used by real people, with a custom C# layout engine instead of a JS graph library. It's the proof that this isn't theoretical: I ship and maintain real products, not just deliver client specs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Combination Actually Matters for You
&lt;/h2&gt;

&lt;p&gt;Most small businesses get stuck choosing between two bad options: a cheap templated site that can't grow past a certain point, or an agency quote sized for a budget they don't have. Custom development from someone who brings enterprise-grade practices earns its cost specifically in the gap between those two — real integrations, real security, a site built to hold up under actual traffic and scrutiny, at project-based pricing (typically &lt;a href="https://dougrosenbergdev.com/services" rel="noopener noreferrer"&gt;$750–$3,000&lt;/a&gt;, scoped after a free discovery call) instead of an enterprise retainer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need a fully custom build, or would a website builder work fine for my business?
&lt;/h3&gt;

&lt;p&gt;Sometimes a builder is the right call — both Sonus Construction Group and Hardware Etc, above, are built on Squarespace, and that was the correct choice for those projects. Custom development earns its cost when you need something a template genuinely can't do: real integrations, custom functionality, or a site that has to hold up under real traffic and security scrutiny.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does a small business web project actually cost?
&lt;/h3&gt;

&lt;p&gt;Project-based custom builds typically run $750–$3,000, scoped after a free discovery call — see the full breakdown on &lt;a href="https://dougrosenbergdev.com/services" rel="noopener noreferrer"&gt;Services&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does enterprise ERP experience matter for a small business website?
&lt;/h3&gt;

&lt;p&gt;Because the failure modes are the same shape at different stakes — a wrong report relied on by 500 staff and a small business site going down during its busiest week both come down to the same discipline: test it, verify it, don't assume it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you maintain the site after launch?
&lt;/h3&gt;

&lt;p&gt;Yes — ongoing support is available as a monthly retainer covering updates, security patches, and small enhancements; see &lt;a href="https://dougrosenbergdev.com/consulting" rel="noopener noreferrer"&gt;Consulting&lt;/a&gt; for details.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;If this sounds like the kind of discipline you want behind your next site, the fastest way to find out is a free discovery call — reach out via &lt;a href="https://dougrosenbergdev.com/consulting" rel="noopener noreferrer"&gt;Consulting&lt;/a&gt; or &lt;a href="https://dougrosenbergdev.com/services" rel="noopener noreferrer"&gt;Services&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>freelance</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
