<?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: SIKOUTRIS</title>
    <description>The latest articles on DEV Community by SIKOUTRIS (@julien786534).</description>
    <link>https://dev.to/julien786534</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%2F3783172%2Fd957c4d9-9565-46f3-8256-313231657389.png</url>
      <title>DEV Community: SIKOUTRIS</title>
      <link>https://dev.to/julien786534</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/julien786534"/>
    <language>en</language>
    <item>
      <title>What OSHA Actually Means by "Immediately Accessible" SDS Access (And What Breaks When the Power Goes Out)</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Mon, 09 Nov 2026 10:25:48 +0000</pubDate>
      <link>https://dev.to/julien786534/what-osha-actually-means-by-immediately-accessible-sds-access-and-what-breaks-when-the-power-4ioc</link>
      <guid>https://dev.to/julien786534/what-osha-actually-means-by-immediately-accessible-sds-access-and-what-breaks-when-the-power-4ioc</guid>
      <description>&lt;p&gt;Ask five safety managers what "immediately accessible" means for Safety Data Sheets and you'll get five different answers. A binder in the break room. A laminated sheet taped to a drum. A QR code on a chemical cabinet. A login screen nobody remembers the password to. Which one of these actually satisfies OSHA?&lt;/p&gt;

&lt;p&gt;We've spent the last few months building toward an answer, because the regulatory text turns out to be both shorter and stricter than most of the industry treats it.&lt;/p&gt;

&lt;p&gt;Start with the actual sentence. 29 CFR 1910.1200(g)(8) says the employer "shall maintain in the workplace copies of the required safety data sheets for each hazardous chemical, and shall ensure that they are readily accessible during each work shift to employees when they are in their work area(s)." That's the whole requirement. No mention of binders, no mention of software, no mention of barcodes. The standard is performance-based — it tells you the outcome it wants, not the format you have to use to get there.&lt;/p&gt;

&lt;p&gt;So where does "immediately" come from, since the regulation text itself says "readily accessible"? From OSHA's own interpretation letters. In a 1998 letter about using a telephone to back up electronic SDS access, OSHA stated plainly that it interprets "readily accessible" to mean "immediate," and that a two-hour interval between an employee's request and receipt of the SDS "would fall short of this criterion." A follow-up letter from February 1999 repeats almost the same language: ready accessibility means the employee can read and refer to the information right then, not after a phone call and a wait.&lt;/p&gt;

&lt;p&gt;That's the actual bar. Not "available somewhere in the building." Not "can be emailed over." Immediate, in the work area, during the shift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digital access is fine, OSHA said so in 1996
&lt;/h2&gt;

&lt;p&gt;Here's the part that surprises people who assume paper is the safer compliance choice: OSHA settled the electronic-versus-paper question decades ago. A 1996 standard interpretation letter is explicit that electronic access, microfiche, and other alternatives to paper copies "are permitted as long as no barriers to immediate employee access in each workplace are created by such options." Computer terminal access, fax, any readable on-site method — all acceptable, as long as nothing stands between the worker and the information.&lt;/p&gt;

&lt;p&gt;Notice the condition, though. It isn't "digital is automatically compliant." It's "digital is compliant as long as there's no barrier." A tablet that needs a password nobody has, a system only one manager can log into, a scanner app that needs a cell signal in a basement with none — those are barriers. The format isn't the test. The barrier is the test.&lt;/p&gt;

&lt;p&gt;Which brings us to the question that actually keeps us up at night while building this: what happens when the system itself goes down?&lt;/p&gt;

&lt;h2&gt;
  
  
  Power outages, dead wifi, and the backup clause nobody reads
&lt;/h2&gt;

&lt;p&gt;This is the detail most SDS software marketing conveniently skips, and it's the one we think matters most. OSHA has directly addressed what happens when a primary electronic system fails, and the answer isn't "you're out of compliance until IT fixes it." In that same February 1999 interpretation, OSHA describes the one scenario where a delay longer than immediate gets tolerated: when the primary system has failed — the letter contemplates things like power outages and equipment failure — and a backup, telephone transmittal in that example, delivers the SDS "as soon as possible." The exception exists, but it's narrow, and it only covers you if you actually have a working backup path, not just a hope that the outage will be short.&lt;/p&gt;

&lt;p&gt;Translate that into system design and it stops being a legal footnote and turns into an architecture requirement. If SDS access lives entirely behind a login screen on a server you don't control, a cloud outage doesn't just slow things down — it potentially puts every shift supervisor in violation the moment someone needs a sheet and can't get one. We've built local, on-device caching into &lt;a href="https://mysdsmanager.com" rel="noopener noreferrer"&gt;the SDS compliance tool we're building&lt;/a&gt; for exactly this reason, so a dead connection doesn't automatically turn into a dead stop.&lt;/p&gt;

&lt;p&gt;Field and mobile crews are the other case people underestimate, and OSHA has guidance going back to 1990 on exactly this one. A Minnesota poison center once asked OSHA directly: if traveling workers get a toll-free number to a central MSDS bank, plus fax backup, is that enough on its own? OSHA's answer was more conditional than most vendors would like it to be. The agency's reply ties the phone line to the &lt;em&gt;mobile worksite&lt;/em&gt; provision specifically, and is explicit that "employees have access to the MSDSs prior to leaving the primary workplace, and when they return, the telephone system is simply an emergency arrangement." In other words: the toll-free number is backup for the trip, not a replacement for having already had access to the sheet. Phone-to-a-database doesn't retroactively create accessibility you skipped earlier.&lt;/p&gt;

&lt;p&gt;The letter predates smartphones by two decades, but the logic hasn't moved: a dashboard you can pull up mid-shift from a job site isn't the same as a system you can only reach when someone picks up the phone on the other end. If a mobile crew's only path to an SDS is a network call to a human, you haven't really replaced the binder — you've just made the backup slower and harder to lose track of.&lt;/p&gt;

&lt;p&gt;None of this is exotic interpretation. It's sitting in OSHA's own published letters, most of it decades old, mostly ignored by vendors who'd rather talk about QR codes than about what happens when the QR code's backend times out. We'd rather build toward the actual requirement than around it — and that means treating "immediately accessible" as a systems problem, not a marketing checkbox.&lt;/p&gt;

</description>
      <category>safety</category>
      <category>compliance</category>
      <category>buildinpublic</category>
      <category>saas</category>
    </item>
    <item>
      <title>Germany May Scrap Its 20-Employee DPO Rule in 2026 — Which Makes the Decision Logic Harder, Not Easier</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Fri, 06 Nov 2026 11:37:57 +0000</pubDate>
      <link>https://dev.to/julien786534/germany-may-scrap-its-20-employee-dpo-rule-in-2026-which-makes-the-decision-logic-harder-not-147a</link>
      <guid>https://dev.to/julien786534/germany-may-scrap-its-20-employee-dpo-rule-in-2026-which-makes-the-decision-logic-harder-not-147a</guid>
      <description>&lt;p&gt;On December 4, 2025, Germany's federal and state governments signed off on a "Modernisierungsagenda" that includes, as point 160, a commitment: the federal government will submit a bill to repeal § 38 Abs. 1 BDSG by December 31, 2026.&lt;/p&gt;

&lt;p&gt;That single paragraph of a coalition document is the kind of thing that matters a lot if you run a site that tries to tell German businesses, in plain language, which compliance officer they're legally required to appoint. I maintain &lt;a href="https://beauftragten-finder.de/datenschutzbeauftragter-pflicht-kmu-2026" rel="noopener noreferrer"&gt;beauftragten-finder.de&lt;/a&gt;, a guide site covering exactly that question — Datenschutzbeauftragter, Brandschutzbeauftragter, and the half-dozen other "Beauftragte" roles German law scatters across different statutes. § 38 BDSG is the one with the cleanest rule of all of them, which is exactly why losing it is interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule as it stands today
&lt;/h2&gt;

&lt;p&gt;§ 38 Abs. 1 BDSG requires a company to appoint a data protection officer if at least 20 people are "constantly engaged in the automated processing of personal data." Two details trip people up every time: it's not headcount, it's people who &lt;em&gt;regularly handle&lt;/em&gt; personal data (so part-time HR or customer-service staff count, a warehouse crew mostly doesn't); and the threshold itself moved once already, from 10 to 20, at the end of 2019. It is, as these things go, a genuinely simple rule to encode: count the relevant employees, compare to 20, done. A website can ask three questions and give a correct answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the 2026 proposal actually changes
&lt;/h2&gt;

&lt;p&gt;The Modernisierungsagenda commitment is to &lt;em&gt;submit a bill&lt;/em&gt;, not to have passed one — as of writing, no legal change has entered into force, and several outlets covering it (dr-datenschutz.de among them) are careful to say so. If it does go through roughly as described, the German-specific headcount trigger disappears, and private-sector DPO obligations fall back entirely on Article 37 GDPR.&lt;/p&gt;

&lt;p&gt;Article 37 doesn't count employees. It asks whether your &lt;em&gt;core activities&lt;/em&gt; involve "regular and systematic monitoring of data subjects on a large scale," or large-scale processing of special-category data (health records, biometrics, criminal records, and the like). Those are judgment calls about what a business actually does, not a number you can pull from payroll.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why that's a harder problem to put on a page
&lt;/h2&gt;

&lt;p&gt;Given my day job, this is the part I find genuinely interesting rather than just newsworthy. A threshold rule like "20 employees" is a solved problem for a decision-guide site: one comparison, one answer, and the answer doesn't need a caveat. "Large-scale systematic monitoring" is not that. A 15-person startup running a behavioral ad-tech product that profiles every visitor on the web might clear the Article 37 bar easily, while a 50-person logistics company with a straightforward payroll system might not come close — and no amount of yes/no questions fully resolves that without a few paragraphs of "it depends, and here's what regulators have actually said."&lt;/p&gt;

&lt;p&gt;That's a different kind of content than a threshold checker: less a calculator, more a set of worked examples and the EDPB's own guidance on what counts as "large scale" (volume of data subjects, duration, geographic extent, among other factors). If § 38 does go away, the honest version of a site like ours has to replace a clean gate with exactly that — which is more work to write and harder to get wrong, but also a more accurate reflection of what German and EU law actually ask of a business.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical takeaway, regardless of what happens in 2026
&lt;/h2&gt;

&lt;p&gt;Article 37 already applies today, on top of § 38, to any company whose core activity fits its criteria — headcount has never been a safe harbor for that half of the rule. If the German threshold disappears, nothing about your actual processing changes; only the easy way of ruling yourself out does. Companies currently relying on "we have fewer than 20 people" as their only answer are the ones who should look at Article 37 now, not wait for a bill that, as of this writing, hasn't been introduced.&lt;/p&gt;

&lt;p&gt;We're tracking the proposal and will update &lt;a href="https://beauftragten-finder.de/datenschutzbeauftragter-pflicht-kmu-2026" rel="noopener noreferrer"&gt;the KMU-specific guide&lt;/a&gt; if and when an actual draft bill appears.&lt;/p&gt;

</description>
      <category>gdpr</category>
      <category>privacy</category>
      <category>legal</category>
      <category>compliance</category>
    </item>
    <item>
      <title>The EU AI Act Isn't One Law, It's Four — Modeling Graduated Obligations as Content</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Wed, 04 Nov 2026 09:46:14 +0000</pubDate>
      <link>https://dev.to/julien786534/the-eu-ai-act-isnt-one-law-its-four-modeling-graduated-obligations-as-content-4bl6</link>
      <guid>https://dev.to/julien786534/the-eu-ai-act-isnt-one-law-its-four-modeling-graduated-obligations-as-content-4bl6</guid>
      <description>&lt;p&gt;Most regulations are annoying to read but structurally simple: one set of rules, one deadline, done. The EU AI Act (Regulation 2024/1689) is a different kind of problem. It doesn't impose one obligation — it imposes four parallel sets of obligations, each gated by a different risk tier, each with its own deadline, and each aimed at a different actor in the chain (the provider who builds the system vs. the deployer who runs it).&lt;/p&gt;

&lt;p&gt;I spent some time recently turning that structure into a plain, static reference site (reglementation-ia.fr) for AI Act compliance, and the interesting part wasn't the legal research — it was deciding how to model the regulation so the content didn't collapse into a wall of text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four tiers, not one checklist
&lt;/h2&gt;

&lt;p&gt;The Act splits AI systems into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prohibited practices&lt;/strong&gt; (Article 5) — a short, closed list, immediate effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-risk systems&lt;/strong&gt; (Annexes I &amp;amp; III) — the heavy one: eight core obligations for providers (risk management, data governance, technical documentation under Annex IV, automated logging, clear instructions for deployers, human oversight, accuracy/robustness testing, a quality management system, plus CE marking and EU database registration), and a separate, shorter list for deployers (use the system as instructed, maintain competent human oversight, validate input data, monitor operation, retain logs for six months, run a rights impact assessment if you're a public body or essential-service operator, and notify affected individuals).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited-risk systems&lt;/strong&gt; (Article 50) — mostly transparency: tell the user they're talking to a chatbot, that content is AI-generated, or that emotion-recognition/biometric categorization is in use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;General-purpose AI models&lt;/strong&gt; — a track of their own: documentation, copyright compliance, training-data transparency, and for models flagged as "systemic risk," adversarial testing and incident reporting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tier also moves on its own clock. High-risk obligations split again depending on which Annex applies (Annex III systems land on one date, Annex I-embedded systems land later). GPAI systemic-risk obligations were already live well before either of those.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why that breaks a normal content model
&lt;/h2&gt;

&lt;p&gt;A typical "guide" page structure — one article per topic — actively hides this. If you write "Obligations for AI Providers" as a single page, you either flatten the risk tiers (inaccurate) or bury four different deadlines in one wall of prose (unreadable, and in a compliance context, genuinely risky — someone will read the wrong paragraph and assume the wrong deadline applies to them).&lt;/p&gt;

&lt;p&gt;What worked better was treating &lt;strong&gt;(tier × actor)&lt;/strong&gt; as the actual content unit, not "topic." Each combination — high-risk/provider, high-risk/deployer, limited-risk/user-facing, GPAI/model-provider — gets its own obligations list, its own applicable date, and its own one-line "who this is" test at the top, so a reader can rule themselves out in one sentence instead of reading the whole page to find out it doesn't apply to them.&lt;/p&gt;

&lt;p&gt;The practical build is unglamorous: plain PHP, no framework, obligations stored as structured per-tier arrays rather than free text, so the same data can render both a "by risk tier" page and a "by deadline" calendar view without maintaining the facts twice. That duplication — the same obligation text copy-pasted into two different narrative pages — is exactly the kind of drift that makes a compliance reference untrustworthy six months after a clarification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The boring lesson
&lt;/h2&gt;

&lt;p&gt;None of this required clever engineering. It required resisting the urge to write the regulation as an essay, and instead modeling it as the thing it actually is: a small matrix of (who you are) × (what tier you're in) × (what date applies to you). The full breakdown, with the eight provider obligations and the shorter deployer list, is up at &lt;a href="https://reglementation-ia.fr/obligations-par-risque" rel="noopener noreferrer"&gt;reglementation-ia.fr/obligations-par-risque&lt;/a&gt; if you're trying to figure out which column of that matrix you're actually in.&lt;/p&gt;

&lt;p&gt;If you're building anything that has to represent tiered regulation — AI Act, but also things like CSRD or the EU Data Act — I'd genuinely recommend modeling by (actor × tier) from day one. Retrofitting it after the content is already written as prose is much more painful than it looks.&lt;/p&gt;

</description>
      <category>compliance</category>
      <category>ai</category>
      <category>legaltech</category>
      <category>php</category>
    </item>
    <item>
      <title>Tracking which EU countries actually missed the Green Claims Directive deadline</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Mon, 02 Nov 2026 10:27:11 +0000</pubDate>
      <link>https://dev.to/julien786534/tracking-which-eu-countries-actually-missed-the-green-claims-directive-deadline-5h4a</link>
      <guid>https://dev.to/julien786534/tracking-which-eu-countries-actually-missed-the-green-claims-directive-deadline-5h4a</guid>
      <description>&lt;p&gt;I maintain a small compliance tool, &lt;a href="https://greenwashing-checker.com" rel="noopener noreferrer"&gt;greenwashing-checker.com&lt;/a&gt;, that scans marketing copy for language likely to run afoul of EU greenwashing rules. Over the past few months the most useful part of the product turned out not to be the scanner itself, but a boring side artifact: a per-country tracker of how EU member states are (or aren't) implementing Directive (EU) 2024/825, the "Empowering Consumers for the Green Transition" directive.&lt;/p&gt;

&lt;p&gt;Here's the data problem that pushed me to build it, and what the latest enforcement update actually says.&lt;/p&gt;

&lt;h2&gt;
  
  
  The directive, in one paragraph
&lt;/h2&gt;

&lt;p&gt;Directive (EU) 2024/825 bans a long list of vague environmental claims ("eco-friendly", "climate neutral" without substantiation, future-based claims with no concrete plan) and requires member states to transpose it into national law. The transposition deadline was &lt;strong&gt;27 March 2026&lt;/strong&gt;. The substantive rules become applicable EU-wide on &lt;strong&gt;27 September 2026&lt;/strong&gt;, independent of whether a given country finished its paperwork on time.&lt;/p&gt;

&lt;p&gt;That gap between transposition deadline and application date is exactly the kind of detail that gets lost between a law firm's client alert and a generic "greenwashing is coming" blog post. Nobody was publishing a simple answer to "did my country actually do this on time."&lt;/p&gt;

&lt;h2&gt;
  
  
  What the May enforcement notice showed
&lt;/h2&gt;

&lt;p&gt;On &lt;strong&gt;28 May 2026&lt;/strong&gt;, the European Commission sent letters of formal notice -- the first formal step of an infringement procedure -- to &lt;strong&gt;20 member states&lt;/strong&gt; for failing to notify complete transposition measures. That's 20 out of 27. Those states had two months from the notice to respond and demonstrate full implementation, or face the next stage (a reasoned opinion).&lt;/p&gt;

&lt;p&gt;A handful of countries, including Germany and Italy, had already notified complete transposition before the deadline and weren't on the list. Everyone else was somewhere between "partially transposed" and "hasn't started."&lt;/p&gt;

&lt;p&gt;For a compliance tool aimed at marketing and legal teams, this is the single most actionable fact in the whole directive right now: being in a country that missed the deadline does not mean the rules don't apply to you come September. It just means your national enforcement mechanism -- which regulator, what penalty range, what process -- might still be undefined when the substantive ban kicks in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built a structured tracker instead of writing another explainer
&lt;/h2&gt;

&lt;p&gt;Every law firm covering this directive publishes the same three facts (ban list, deadline, application date) wrapped in their own commentary. What's actually missing is a structured, per-country view that's easy to re-check without re-reading ten PDFs: transposition status, the date of the Commission's notice (if any), and a link back to the primary source.&lt;/p&gt;

&lt;p&gt;The tracker is a flat JSON file per country, rebuilt whenever there's a new Commission action, that feeds a comparison table on the site. It's deliberately dumb -- no scraping, no scoring algorithm, just a maintained structured record cross-referenced against official Commission communications and corroborated against two or three independent legal trackers before publishing an update. Each entry links to its primary source rather than asserting a status on my own authority.&lt;/p&gt;

&lt;p&gt;It's not a sophisticated pipeline. It's the kind of thing that's genuinely useful mostly because almost nobody bothers to keep it current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes next
&lt;/h2&gt;

&lt;p&gt;The two-month response window from the 28 May notice puts the next real checkpoint at the end of July, when the Commission decides whether to escalate to reasoned opinions for countries that still haven't demonstrated full transposition. I'll update the tracker when that happens, since "formal notice" and "reasoned opinion" are meaningfully different stages with different timelines attached.&lt;/p&gt;

&lt;p&gt;If you're tracking this directive for compliance purposes and notice the data is stale or wrong for your country, I'd genuinely like to know -- the &lt;a href="https://greenwashing-checker.com/en/blog/ecgt-member-state-transposition-tracker-2026/" rel="noopener noreferrer"&gt;full tracker is here&lt;/a&gt;, and corrections get fixed same-day.&lt;/p&gt;

</description>
      <category>compliance</category>
      <category>eu</category>
      <category>sustainability</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Running Hyperlocal French News Sites Without a Newsroom</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Fri, 30 Oct 2026 11:38:02 +0000</pubDate>
      <link>https://dev.to/julien786534/running-hyperlocal-french-news-sites-without-a-newsroom-4aaf</link>
      <guid>https://dev.to/julien786534/running-hyperlocal-french-news-sites-without-a-newsroom-4aaf</guid>
      <description>&lt;p&gt;Most small French towns lost their dedicated local newspaper years ago. What's left — town council schedules, local fuel prices, weather, who got married, who passed away — is scattered across town hall PDFs, regional newspaper archives behind paywalls, and Facebook groups. We built a small PHP platform to pull that information back into one place, town by town, and &lt;a href="https://digne-infos.fr" rel="noopener noreferrer"&gt;digne-infos.fr&lt;/a&gt; (covering Digne-les-Bains and the Alpes-de-Haute-Provence) is one of the sites running on it.&lt;/p&gt;

&lt;p&gt;I want to describe the architecture honestly, including the part that's uncomfortable to say out loud: the articles are compiled automatically from public sources, and the site says so on every page. There's no newsroom behind this. What there is, is a pipeline worth describing.&lt;/p&gt;

&lt;h2&gt;
  
  
  No CMS, on purpose
&lt;/h2&gt;

&lt;p&gt;Each town site is a thin PHP application — no WordPress, no page builder. A handful of templates (article, category index, local agenda, obituary/"carnet" notices) get populated from a content pipeline that ingests public sources: town hall bulletins, prefecture announcements, open government feeds. Every generated article cites its source at the bottom. We made the "no human pre-review" fact visible on the site itself rather than let it be inferred, because disclosing it up front is cheaper than being caught hiding it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulling in live public data
&lt;/h2&gt;

&lt;p&gt;Two widgets forced us to integrate with real government APIs instead of shipping static placeholder data: a weather panel (Open-Meteo) and a fuel-price panel built on top of data.economie.gouv.fr, France's official open dataset for fuel prices. Both refresh on a schedule rather than at request time — a local news site doesn't need sub-minute freshness, and caching the upstream response keeps pages fast without hammering a public API meant for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sitemap is a hybrid, and that's deliberate
&lt;/h2&gt;

&lt;p&gt;Google's news sitemap protocol wants a dedicated &lt;code&gt;&amp;lt;news:news&amp;gt;&lt;/code&gt; feed, separate from a normal sitemap, listing only articles published in roughly the last two days. In practice, mixing it into the general sitemap — plain &lt;code&gt;&amp;lt;url&amp;gt;&lt;/code&gt; entries for category and service pages, &lt;code&gt;&amp;lt;news:news&amp;gt;&lt;/code&gt; tags attached only to recent article entries — turned out to be the pragmatic middle ground for a small site that doesn't publish often enough to justify maintaining a second feed pipeline. Full disclosure: our adoption of the news tag is still partial, we're rolling it out article type by article type rather than all at once, and that's a conscious trade-off against building infrastructure for traffic we don't have yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five towns, one codebase
&lt;/h2&gt;

&lt;p&gt;Digne-les-Bains isn't the only town running this stack — the same codebase also powers sites for Aubenas, Manosque, Gap and Aurillac. Each instance is a config object, not a fork: town name, department, the lat/long pair for the weather API, the department filter for the fuel-price feed, and which category templates actually make sense for a town that size (a town of a few thousand people doesn't need a dedicated sports section). Keeping it as config instead of five diverging codebases is what makes it possible for one person to keep all of them running.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we'd do differently
&lt;/h2&gt;

&lt;p&gt;If I were starting a sixth town site today, I'd build the source-citation block as a first-class template field from day one instead of retrofitting it. Disclosure isn't a legal checkbox here — a reader checking "is this real" is the whole trust model for an automated local news site, and the citation is the only thing standing between "aggregator" and "content farm" in that reader's mind.&lt;/p&gt;

&lt;p&gt;None of this replaces an actual reporter sitting through an actual town council meeting. It does mean a resident of a town that lost its newspaper can still find the diesel price three streets over and whether there's a market this Saturday, sourced and dated, without digging through five different town hall PDFs.&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>news</category>
    </item>
    <item>
      <title>Mapping Healthcare Access Across Belgium's 50 Largest Cities: A Data Structuring Problem</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Wed, 28 Oct 2026 09:46:04 +0000</pubDate>
      <link>https://dev.to/julien786534/mapping-healthcare-access-across-belgiums-50-largest-cities-a-data-structuring-problem-3lob</link>
      <guid>https://dev.to/julien786534/mapping-healthcare-access-across-belgiums-50-largest-cities-a-data-structuring-problem-3lob</guid>
      <description>&lt;p&gt;Finding a doctor, dentist or physiotherapist in Belgium sounds like it should be a solved problem. In practice, provider information is scattered across hospital websites, regional health network pages, insurance-fund (mutualiteit/mutuelle) portals and municipal pages — each with its own structure, its own update rhythm, and often only one of the country's two main languages. Building a usable directory meant treating this as a data consolidation problem before it became a product problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting from the geography, not the listings
&lt;/h2&gt;

&lt;p&gt;The first design decision was to anchor the directory on cities rather than on providers. Belgium's healthcare landscape is dense but unevenly distributed: a resident of a large city has a very different search experience than someone in a smaller municipality who needs to know which neighboring city actually has the specialist they're looking for. So the structure became city-first, specialty-second: fifty of the country's largest cities, each with its own page listing the relevant specialties available there, rather than one giant searchable table.&lt;/p&gt;

&lt;p&gt;This sounds like a minor information-architecture choice, but it changes how you source and verify data. A city-first structure forces you to confirm, city by city, which specialties are genuinely represented before publishing anything — it discourages copy-pasting a national list and hoping it's locally accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bilingual problem is not just translation
&lt;/h2&gt;

&lt;p&gt;Belgium's healthcare system is legally and administratively split enough that a straight Dutch-to-French translation of content is not sufficient. Reimbursement rules run through RIZIV/INAMI at the federal level, but a lot of practical patient-facing information — which GP is taking new patients, how a mutualiteit processes a given reimbursement — is only published in the region's dominant language, and sometimes only as a PDF on a local organization's site. Treating language as a content-sourcing constraint, not a localization afterthought, changed which sources were usable at all for the Dutch-speaking pages this site focuses on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explaining the system is part of the directory
&lt;/h2&gt;

&lt;p&gt;A directory of providers is only half useful if visitors don't understand what they're looking at — specifically, what's covered by compulsory insurance (verplichte verzekering) versus what depends on supplementary coverage, and what a referral from a GP changes about reimbursement for a specialist visit. So alongside the city/specialty listings, the site carries plain-language explainers on how Belgian health insurance and patient rights actually work. This content is intentionally conservative: no specific reimbursement amounts are quoted unless they come directly from an official RIZIV/INAMI source, because those figures change and a stale number is worse than no number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it accurate over time
&lt;/h2&gt;

&lt;p&gt;The hard part of a directory site isn't the initial build, it's staying correct as practices close, specialists move cities, or a hospital reorganizes its departments. The approach taken here is deliberately narrow in scope — fewer cities and specialties, verified more carefully — rather than exhaustive coverage that degrades silently. It's a trade-off that will keep mattering as the directory grows: better to be confidently right about fifty cities than vaguely right about the whole country.&lt;/p&gt;

&lt;p&gt;If you're working on a similar problem — structuring fragmented, multi-source, multi-language public-interest data — the lesson that generalizes best is to let the geography or the regulatory boundary drive your data model, not the other way around. You can see the current result at &lt;a href="https://zorg-portaal.be" rel="noopener noreferrer"&gt;Zorg-Portaal&lt;/a&gt;, a healthcare provider directory for Belgium's largest cities.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opendata</category>
      <category>healthcare</category>
      <category>belgium</category>
    </item>
    <item>
      <title>Sora Hit #1 on the App Store, Then Died in Six Months: What That Does to a Comparison Site</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Sat, 24 Oct 2026 14:09:49 +0000</pubDate>
      <link>https://dev.to/julien786534/sora-hit-1-on-the-app-store-then-died-in-six-months-what-that-does-to-a-comparison-site-4a37</link>
      <guid>https://dev.to/julien786534/sora-hit-1-on-the-app-store-then-died-in-six-months-what-that-does-to-a-comparison-site-4a37</guid>
      <description>&lt;p&gt;Last month, our AI video tools comparison site published a guide titled "Sora vs Runway vs Pika 2026." By the time the FAQ section was finished, one of the three tools in the title had already stopped existing.&lt;/p&gt;

&lt;p&gt;I maintain &lt;a href="https://aivideocompare.com" rel="noopener noreferrer"&gt;aivideocompare.com&lt;/a&gt;, an independent comparison site covering 25+ AI video tools across generation, editing, avatars, subtitles and short-form content. No affiliate rankings, no pay-to-play placement -- just pricing and feature tables we try to keep honest. That "try" is the hard part right now, and Sora is the clearest example of why.&lt;/p&gt;

&lt;h2&gt;
  
  
  A product that died faster than our review cycle
&lt;/h2&gt;

&lt;p&gt;OpenAI launched Sora as a standalone app, and it briefly hit #1 on the US App Store. Then the numbers turned: month-over-month declines in installs and spend starting at the beginning of 2026, including a reported 32% drop in new downloads between November and December. OpenAI announced the shutdown on March 24, 2026. The web app and mobile app were discontinued on April 26, 2026. The API followed on September 24, 2026. A reported $1B partnership with Disney went with it.&lt;/p&gt;

&lt;p&gt;Six months from launch to teardown is not a normal product lifecycle. For a comparison site, it means a page we wrote in good faith -- citing real pricing, real output quality, real use cases -- described a tool that, within the same calendar year, stopped being something you could sign up for.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not just Sora
&lt;/h2&gt;

&lt;p&gt;Google's video line moved almost as fast, just less dramatically. The &lt;code&gt;veo-3.0-generate-001&lt;/code&gt; and &lt;code&gt;veo-3.0-fast-generate-001&lt;/code&gt; endpoints were deprecated on June 30, 2026 in favor of &lt;code&gt;veo-3.1-generate-001&lt;/code&gt; / &lt;code&gt;veo-3.1-fast-generate-001&lt;/code&gt;. Then, by October, the Veo 3.1 preview models on the Gemini API themselves showed up with shutdown notices, with traffic being pointed toward &lt;code&gt;gemini-omni-1.1-flash&lt;/code&gt; instead. That's three named "state of the art" generations in about seven months, on the model line that's supposed to be the stable alternative to Sora.&lt;/p&gt;

&lt;p&gt;When the tools you're comparing turn over that fast, "best AI video generator 2026" isn't a one-time piece of content. It's closer to a changelog you maintain for as long as the page stays up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quieter problem: you can't always read the price
&lt;/h2&gt;

&lt;p&gt;Model churn is the visible failure mode. The one that actually eats more of our time is pricing verification, because several vendors make it adversarial by accident or by design. Auditing the pricing tables for our comparison guide, we hit three distinct failure patterns in tools that are very much still alive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CapCut&lt;/strong&gt;: the official pricing page returns a 404 when fetched directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VEED.IO&lt;/strong&gt;: pricing tiers render client-side -- there are no figures in the HTML actually served to a basic request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fliki&lt;/strong&gt;: the free plan is confirmed and stable, but paid tiers load dynamically, so a static fetch gives you half the picture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are "the product changed underneath us" problems. They're "you cannot even read the current price without running a full browser" problems. For a site that promises no pay-to-play rankings, that's not a cosmetic issue -- publishing a stale or wrong price is the exact failure mode we're trying to avoid by not taking money for placement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes about how we build the page
&lt;/h2&gt;

&lt;p&gt;Two things, concretely. First, any comparison title that names specific products needs a recheck cadence baked in, not a "write once" assumption -- a tool named in a 2026 headline can be gone by the time the headline is still ranking. Second, pricing data that only renders client-side can't be treated the same as a static price list; it needs either a documented "last verified by hand on [date]" note or a different data source, because silently serving a 404-derived blank isn't better than not listing the tool at all.&lt;/p&gt;

&lt;p&gt;None of this is a reason to stop doing honest comparisons -- if anything it's the opposite. A site that still says "Sora vs Runway vs Pika" without acknowledging Sora is gone is worse than useless; it actively misleads whoever's trying to pick a tool today. We keep &lt;a href="https://aivideocompare.com/guide/sora-vs-runway-vs-pika-2026/" rel="noopener noreferrer"&gt;the guide&lt;/a&gt; up, but the real product isn't the comparison table -- it's the discipline of going back and fixing it when the ground moves, which in this category is most months.&lt;/p&gt;

&lt;p&gt;If you maintain a comparison or review site in a category with this much product churn, I'd be curious how you're handling re-verification. We're still mostly doing it by hand.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>saas</category>
    </item>
    <item>
      <title>Why a black box doesn't redact a PDF (and how to remove the text for real with MuPDF WebAssembly)</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Fri, 23 Oct 2026 16:17:12 +0000</pubDate>
      <link>https://dev.to/julien786534/why-a-black-box-doesnt-redact-a-pdf-and-how-to-remove-the-text-for-real-with-mupdf-webassembly-2fma</link>
      <guid>https://dev.to/julien786534/why-a-black-box-doesnt-redact-a-pdf-and-how-to-remove-the-text-for-real-with-mupdf-webassembly-2fma</guid>
      <description>&lt;p&gt;A black rectangle over a line of text in a PDF hides nothing. It sits in a layer on top of the page, and the text underneath is still there: you can select it, copy it, search for it, or extract it with a single command. In January 2019, a court filing by Paul Manafort's lawyers was "redacted" this way, and journalists read the hidden passages by copying and pasting them.&lt;/p&gt;

&lt;p&gt;I ran into this while building a browser-based redaction tool, so here is what actually happens inside the file, and how to remove text for real with MuPDF compiled to WebAssembly, without uploading the document anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the black box fails
&lt;/h2&gt;

&lt;p&gt;A PDF page is a content stream: a list of drawing operators. Text is drawn by operators like &lt;code&gt;Tj&lt;/code&gt;, with the glyphs and their positions. When you draw a black rectangle "over" it, most editors do one of two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add a &lt;code&gt;Square&lt;/code&gt; annotation, which lives in the page's &lt;code&gt;/Annots&lt;/code&gt; array, separate from the content stream;&lt;/li&gt;
&lt;li&gt;or append a filled rectangle (&lt;code&gt;re f&lt;/code&gt;) at the end of the content stream.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In both cases the original &lt;code&gt;Tj&lt;/code&gt; operator is untouched. Any text extractor reads the operators, not the pixels, so it ignores the box entirely.&lt;/p&gt;

&lt;p&gt;You can check it in a few lines with the &lt;code&gt;mupdf&lt;/code&gt; npm package (same engine as the browser build):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mupdf&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mupdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mupdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toStructuredText&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;asText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a test file where I covered an IBAN with a black square annotation, this printed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client IBAN: FR76 3000 6000 0112 3456 7890 189
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The box was there on screen. The number was there in the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real redaction: rewrite the content stream
&lt;/h2&gt;

&lt;p&gt;Real redaction means removing the glyphs from the content stream itself, and dealing with anything else that overlaps the area (images, vector drawings). MuPDF does this with &lt;code&gt;Redact&lt;/code&gt; annotations followed by &lt;code&gt;applyRedactions()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;needle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FR76 3000 6000 0112 3456 7890 189&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// a quad is 8 numbers: ul, ur, ll, lr corners&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createAnnotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Redact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyRedactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                    &lt;span class="c1"&gt;// draw a black box where text was&lt;/span&gt;
  &lt;span class="nx"&gt;mupdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PDFPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDACT_IMAGE_PIXELS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// blank the covered pixels of images&lt;/span&gt;
  &lt;span class="nx"&gt;mupdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PDFPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDACT_LINE_ART_NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// keep vector drawings&lt;/span&gt;
  &lt;span class="nx"&gt;mupdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PDFPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDACT_TEXT_REMOVE&lt;/span&gt;         &lt;span class="c1"&gt;// delete the glyphs&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving and reopening, the same extraction gives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client IBAN:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;page.search("FR76")&lt;/code&gt; returns nothing. I also saved the file decompressed and searched the raw bytes: the string is gone.&lt;/p&gt;

&lt;p&gt;Three details matter here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save with garbage collection.&lt;/strong&gt; PDFs can be saved incrementally: changes are appended and the old objects stay in the file. If you save without cleaning, the previous version of the content stream may still be in there. Use options like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveToBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;garbage=4,compress,clean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;asUint8Array&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;garbage=4&lt;/code&gt; drops unreferenced objects and merges duplicates, &lt;code&gt;clean&lt;/code&gt; rewrites the content streams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copy the buffer.&lt;/strong&gt; &lt;code&gt;asUint8Array()&lt;/code&gt; returns a view on the WebAssembly heap. If the heap grows (open a second document, render a page), the view can become invalid and you get an empty or corrupted download. The &lt;code&gt;.slice()&lt;/code&gt; makes a real copy. This one cost me a confusing bug where large files sometimes downloaded as zero bytes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Images are not text.&lt;/strong&gt; If the sensitive data is in a scanned image, &lt;code&gt;REDACT_TEXT_REMOVE&lt;/code&gt; does nothing for it. &lt;code&gt;REDACT_IMAGE_PIXELS&lt;/code&gt; blanks the pixels under the rectangle, which is what you want for a scan. There is no text to find in a scan without OCR, so the user has to draw the zone by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the leak surface
&lt;/h2&gt;

&lt;p&gt;Even with the text gone, a PDF often carries more than its pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;/Info&lt;/code&gt; dictionary and the XMP &lt;code&gt;/Metadata&lt;/code&gt; stream (author, software, sometimes the original file path);&lt;/li&gt;
&lt;li&gt;form fields whose values live in &lt;code&gt;/AcroForm&lt;/code&gt;, not in the page content. Search will not find a value typed in a field unless you flatten the form first, which is how I once missed an IBAN in a test;&lt;/li&gt;
&lt;li&gt;annotations and comments, embedded files, JavaScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deleting &lt;code&gt;/Metadata&lt;/code&gt; from the catalog, emptying &lt;code&gt;/Info&lt;/code&gt;, then saving with &lt;code&gt;garbage=4&lt;/code&gt; removes the old metadata objects as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it in the browser
&lt;/h2&gt;

&lt;p&gt;The whole thing runs client side: the &lt;code&gt;.wasm&lt;/code&gt; file (about 10 MB, cached after the first visit) loads once, the PDF is read from a file input as an &lt;code&gt;ArrayBuffer&lt;/code&gt;, processed in memory, and downloaded with a &lt;code&gt;Blob&lt;/code&gt; URL. Nothing leaves the machine, which is the point when the document is a contract or a medical record. A strict Content Security Policy works as long as you allow &lt;code&gt;'wasm-unsafe-eval'&lt;/code&gt; in &lt;code&gt;script-src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One licensing note: MuPDF is AGPL. If you ship it on a public website, you publish your source code, or you buy a commercial licence from Artifex.&lt;/p&gt;

&lt;p&gt;I put all of this into a free tool, &lt;a href="https://pdfanonym.com/redact-pdf/" rel="noopener noreferrer"&gt;pdfanonym.com&lt;/a&gt;, which runs entirely in the browser. But even if you never use it, try the extraction check above on the last "redacted" PDF you received. The results can be surprising.&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>webassembly</category>
      <category>privacy</category>
      <category>javascript</category>
    </item>
    <item>
      <title>An AI Writing Tool I Reviewed Got Acquired, Renamed, and Quietly Disappeared — So I Audited Pricing On All 55</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Thu, 22 Oct 2026 12:17:03 +0000</pubDate>
      <link>https://dev.to/julien786534/an-ai-writing-tool-i-reviewed-got-acquired-renamed-and-quietly-disappeared-so-i-audited-pricing-3hih</link>
      <guid>https://dev.to/julien786534/an-ai-writing-tool-i-reviewed-got-acquired-renamed-and-quietly-disappeared-so-i-audited-pricing-3hih</guid>
      <description>&lt;p&gt;Narrato was one of the AI writing tools I'd had listed and priced on my AI-writing-tools comparison site since early 2026. It had a content calendar, an AI writing assistant, and its own pricing page. When I went back in October to re-verify its numbers as part of a routine audit, narrato.io didn't load its pricing page at all — it 301-redirected to typeface.ai.&lt;/p&gt;

&lt;p&gt;Narrato was acquired by Typeface in September 2024. There's no public standalone pricing anymore; it's been folded into Typeface's enterprise content platform. I'd been carrying a dead price tag on a product that, as a standalone thing a reader could sign up for, no longer exists.&lt;/p&gt;

&lt;p&gt;That discovery is what turned a routine pricing check into a full audit of all 55 tools on the site, re-verified one by one against each vendor's actual current pricing page rather than whatever I'd written down when the entry was first created. Some of what I found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jasper&lt;/strong&gt;: the Creator plan at $49/month doesn't exist anymore. Current pricing is Pro at $59/month (billed annually) or $69/month (billed monthly), and the "100,000+ clients" marketing claim I'd been repeating had no source I could verify, so it came out rather than staying in as an unsourced number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepL Write&lt;/strong&gt;: I had it listed as "included in DeepL Pro." It isn't. Write Pro is a separate add-on at EUR 6.99/user/month, billed annually, on top of a DeepL Pro subscription.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rytr&lt;/strong&gt;: priced at Saver $9 / Unlimited $29. The real current structure is Unlimited at EUR 7.50/month or Premium at EUR 24.16/month, both billed annually — different currency, different tier names, different numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fliki&lt;/strong&gt;: I had Standard at $21 and Premium at $66. Live pricing is $28 and $88, billed monthly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clearscope&lt;/strong&gt;: listed at $189/month, actually $129/month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are small typos. Each one is a case where the vendor changed something — a plan name, a currency, a billing cadence, an entire go-to-market motion — and the old number kept sitting there looking confident and specific, which is exactly what makes a wrong price more damaging than an honest "contact us."&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that's easy to get wrong when you fix this
&lt;/h2&gt;

&lt;p&gt;The tempting shortcut is to treat "verify pricing" as "update the number." It isn't — it's "read the vendor's current pricing page and write down only what's actually there." A few of the corrections above required reading the page closely enough to notice that a tier had been renamed, not just repriced (DeepL's bundling structure changed, Rytr's billing currency and cadence changed, not just the digits). A find-and-replace on the old number would have produced a technically-updated page that was still wrong in its surrounding sentences.&lt;/p&gt;

&lt;p&gt;The other temptation is to keep a dead entry like Narrato's around because deleting content feels like losing SEO value. I didn't delete it — I rewrote it in the past tense, with its acquisition and redirect documented, rather than leaving a live-sounding pricing table for a product you can no longer actually subscribe to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I'm writing this down
&lt;/h2&gt;

&lt;p&gt;If you maintain any kind of "best AI tools for X" page, the AI tooling market in 2026 is genuinely volatile enough that a once-a-year pricing check isn't enough. Acquisitions fold products into other products. Currencies and billing cadences change without a rename. And the error that costs you the most trust isn't the one that's obviously wrong — it's the one that still looks exactly as confident and specific as it did the day you wrote it.&lt;/p&gt;

&lt;p&gt;The fact-checked comparison, including the updated Narrato entry and all 55 re-verified tool pages, is live at &lt;a href="https://aiwritingcompare.com/guides" rel="noopener noreferrer"&gt;aiwritingcompare.com/guides&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>writing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Windsurf and Codeium Didn't Just Get Acquired — They Disappeared Into Devin</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Thu, 22 Oct 2026 12:16:47 +0000</pubDate>
      <link>https://dev.to/julien786534/windsurf-and-codeium-didnt-just-get-acquired-they-disappeared-into-devin-1f61</link>
      <guid>https://dev.to/julien786534/windsurf-and-codeium-didnt-just-get-acquired-they-disappeared-into-devin-1f61</guid>
      <description>&lt;p&gt;If you've been comparing AI coding assistants any time in the last year, you've probably bookmarked a page for Windsurf or Codeium. I did too — both had dedicated entries on the AI-coding-tools comparison site I maintain, each with their own pricing table, feature list, and "vs Cursor" comparison page.&lt;/p&gt;

&lt;p&gt;Then, while doing a routine pricing re-check in October 2026, I ran &lt;code&gt;curl -L&lt;/code&gt; against both root domains to confirm the numbers were still accurate. Neither one resolved to itself. &lt;code&gt;windsurf.com&lt;/code&gt; and &lt;code&gt;codeium.com&lt;/code&gt; both 301-redirect straight to &lt;code&gt;devin.ai&lt;/code&gt; — Cognition's product, the one best known for its "AI software engineer" positioning.&lt;/p&gt;

&lt;p&gt;This wasn't a rumor I read somewhere. It's the kind of thing you only notice if you're maintaining a dataset that has to stay honest: the vendor's own infrastructure told me, via an HTTP status code, that two products I'd been tracking as separate competitors no longer independently exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;Codeium (the autocomplete/agent company) rebranded to Windsurf earlier in 2026, and Windsurf was then absorbed into Cognition — the company behind Devin — consolidating what used to be three brand names into one. If you go looking for Windsurf or Codeium pricing today, you won't find one. You'll find Devin's pricing instead: a free tier, Pro at $20/month, and Max plans starting at $100/month for 5x or 20x usage, verified directly on devin.ai/pricing.&lt;/p&gt;

&lt;p&gt;For a comparison site, this is a genuinely annoying problem, because "annoying for the maintainer" usually means "actively misleading for the reader" if you don't fix it. Keeping the old Windsurf-vs-Cursor and Codeium-vs-Copilot pages live and un-updated would have meant serving two confidently-written reviews of products that, strictly speaking, don't have an independent pricing page to point to anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  The smaller, uglier discovery
&lt;/h2&gt;

&lt;p&gt;While I was in there fixing the merger, I also re-checked Tabnine, which I had priced at a flat $15/month. That number is stale too — tabnine.com/pricing now redirects to a Tricentis contact form. Tabnine's pivot into enterprise/quote-based pricing isn't as dramatic as a full product merger, but it's the same underlying lesson: a comparison site that doesn't re-verify its sources on a schedule will quietly start lying to its readers, one silently-changed pricing page at a time.&lt;/p&gt;

&lt;p&gt;And the opposite failure mode is just as real. Re-checking Cursor's pricing from its live JSON-LD &lt;code&gt;Offer&lt;/code&gt; schema (rather than trusting what I'd written down months earlier) turned up a full tier structure I'd been missing: Hobby (free), Pro ($20), Pro+ ($60), Ultra ($200), and Teams ($40/user/month). The fabricated-looking "100 reviews" count sitting in the page's structured data had to go too — there was no actual review data behind it, just a stale default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters more for AI coding tools specifically
&lt;/h2&gt;

&lt;p&gt;This category moves faster than most SaaS verticals. In a market where funding rounds and acquisitions happen on a quarterly cadence, a "best AI coding assistant" page that isn't re-verified against the vendor's actual, current pricing page is worse than no page at all — it actively erodes trust in whatever is accurate on the rest of the site.&lt;/p&gt;

&lt;p&gt;The fix, in the end, wasn't complicated: merge the Windsurf and Codeium entries into one sourced "Devin (formerly Windsurf/Codeium)" listing, keep the comparison pages but update their verdicts, and add a visible "price checked on [date], sourced from [vendor URL]" line to every tool page so readers can judge staleness for themselves instead of trusting a number that might be six months old.&lt;/p&gt;

&lt;p&gt;If you maintain any kind of tools directory, comparison site, or "state of X in 2026" page: the acquisitions you don't track are the ones that'll embarrass you. I've started treating root-domain redirects as a signal worth checking on a recurring basis, not just when something looks obviously broken.&lt;/p&gt;

&lt;p&gt;The updated comparison, including the merged Devin/Windsurf/Codeium entry, is live at &lt;a href="https://aicodingcompare.com/alternative/cursor/" rel="noopener noreferrer"&gt;aicodingcompare.com/alternative/cursor&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What Running 70+ Small Sites Solo Taught Me About Choosing Project Management Software</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Tue, 20 Oct 2026 13:10:39 +0000</pubDate>
      <link>https://dev.to/julien786534/what-running-70-small-sites-solo-taught-me-about-choosing-project-management-software-57g5</link>
      <guid>https://dev.to/julien786534/what-running-70-small-sites-solo-taught-me-about-choosing-project-management-software-57g5</guid>
      <description>&lt;p&gt;I run a one-person digital publishing operation: a portfolio of several dozen small, independent websites, each with its own content cadence, maintenance tasks, and SEO work. No team, no account managers, just me and a rotating set of recurring tasks across a lot of unrelated "projects."&lt;/p&gt;

&lt;p&gt;For a long time I tried to manage this with whatever project management tool was popular that year, and every single one of them fought me in the same way: they were built for a 10-person team shipping one product, not for one person juggling 70 small, mostly-independent workstreams.&lt;/p&gt;

&lt;p&gt;Here's what I eventually learned actually matters when you're evaluating PM software at this kind of scale, which is different from the advice aimed at growing teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Per-seat pricing is the wrong lens entirely
&lt;/h3&gt;

&lt;p&gt;Most PM tools price around headcount, which makes sense for a team but is irrelevant when there's one of you. The real cost driver at solo-portfolio scale is how many "projects" or "spaces" you can create before the plan forces you to pay more — because you don't have 1 project, you have dozens. I've been quietly pushed into enterprise tiers by project-count caps more often than by user caps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recurring task templates need to survive at volume
&lt;/h3&gt;

&lt;p&gt;A lot of my work is the same task repeated across many sites on a schedule: a quarterly content audit, a monthly backlink check, a weekly indexation review. Tools that handle recurring tasks gracefully for 5 projects often become unusable at 70, because there's no good way to template a task across many projects at once without either a script against an API or hours of manual setup. If a tool doesn't expose task creation through an API, assume you'll eventually write a workaround or abandon it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data portability matters more than the feature list
&lt;/h3&gt;

&lt;p&gt;When you're the only person who will ever audit your own task history, lock-in risk is personal: if the export is bad, you lose your own institutional memory, not a team's. I now treat "can I get a clean CSV or API dump of everything, including completed task history" as a harder requirement than any kanban/gantt/calendar view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Views built for sprints don't match a maintenance cadence
&lt;/h3&gt;

&lt;p&gt;Sprint-based tools assume you're building toward a release. A lot of small-site operation work isn't sprint-shaped — it's maintenance-shaped: recurring, low-urgency, easy to lose track of across many unrelated properties. A flat, filterable task list that can be sliced by site/tag turned out to matter more to me than any sprint board ever has.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd actually tell someone shopping for this
&lt;/h3&gt;

&lt;p&gt;If you're in a similar spot — a solo operator or very small team managing many small, mostly independent projects rather than one big one — don't start from "what do growing startups use." Start from: can I create and update tasks programmatically, does pricing scale with project count instead of just seats, and can I get my data back out cleanly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://softwarerundown.com/best-project-management-software-2026-comparison" rel="noopener noreferrer"&gt;Software Rundown's project management comparison&lt;/a&gt; is a reasonable starting point if you want a side-by-side on pricing and core features across eight of the mainstream options — useful for narrowing the field before you test the two or three that might actually fit a portfolio-style workflow rather than a single-team one.&lt;/p&gt;

&lt;p&gt;None of this is a knock on PM tools generally — they're built for the use case most of their customers have, which is one team shipping one thing. It just means that if your situation looks like mine, the usual "best PM tool" rankings are optimizing for a different problem than the one you actually have.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>saas</category>
      <category>smallbusiness</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Making Tax Digital for ITSA: what the quarterly API actually wants from your bookkeeping</title>
      <dc:creator>SIKOUTRIS</dc:creator>
      <pubDate>Mon, 19 Oct 2026 09:28:12 +0000</pubDate>
      <link>https://dev.to/julien786534/making-tax-digital-for-itsa-what-the-quarterly-api-actually-wants-from-your-bookkeeping-1on0</link>
      <guid>https://dev.to/julien786534/making-tax-digital-for-itsa-what-the-quarterly-api-actually-wants-from-your-bookkeeping-1on0</guid>
      <description>&lt;p&gt;If you're a UK sole trader or landlord who also happens to write code — and a lot of freelance developers are exactly that — you've probably noticed HMRC's Making Tax Digital for Income Tax Self Assessment (MTD ITSA) creeping into your admin this year. It's live for anyone with qualifying self-employment or property income above £50,000, and it changes something that used to be a once-a-year chore into a quarterly one.&lt;/p&gt;

&lt;p&gt;The first deadline (7 August 2026, covering the 6 April–5 July quarter) has already passed for most people in scope. The next one — 7 November 2026, covering 6 July–5 October — is the one actually worth planning for now, because it's the first time the "quarterly habit" either sticks or doesn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  The deadline math is simple, the formula isn't obvious
&lt;/h3&gt;

&lt;p&gt;HMRC's rule is: the 7th of the second month after the quarter ends. That gives you four fixed dates for a standard tax-year quarter split:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Q1 (6 Apr–5 Jul) → 7 August&lt;/li&gt;
&lt;li&gt;Q2 (6 Jul–5 Oct) → 7 November&lt;/li&gt;
&lt;li&gt;Q3 (6 Oct–5 Jan) → 7 February&lt;/li&gt;
&lt;li&gt;Q4 (6 Jan–5 Apr) → 7 May&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can elect calendar-quarter alignment instead at sign-up, but once you've picked, you're locked into it for the year — so if you're scripting reminders or calendar events, build them from your actual elected quarter type, not the tax-year default.&lt;/p&gt;

&lt;h3&gt;
  
  
  What "a quarterly update" actually is
&lt;/h3&gt;

&lt;p&gt;This trips people up: it is not a detailed ledger submission. HMRC wants a digital summary — categorised totals of income and expenses for the three-month period, submitted through MTD-compatible software via API. No receipts, no line-by-line transaction list, no supporting documents at this stage. That's a deliberate design choice to keep the quarterly burden light; the detail only matters at the year-end final declaration.&lt;/p&gt;

&lt;p&gt;For anyone maintaining their own tracking (a spreadsheet, a small internal tool, a script pulling from a bank feed), the practical implication is that your categorisation scheme matters more than your transaction-level detail. If your expense categories don't map cleanly onto HMRC's self-employment categories (ask yourself: can I produce "total turnover" and "total expenses by type" for this quarter in one query?), you'll be doing manual reclassification work every quarter instead of once a year. Worth fixing before Q2 data entry, not after.&lt;/p&gt;

&lt;h3&gt;
  
  
  The penalty system has a soft landing — for now
&lt;/h3&gt;

&lt;p&gt;For the 2026/27 tax year, there are no penalty points for a late quarterly submission. That changes from April 2027: each missed deadline after that earns a point, four points trigger a £200 fine, and every subsequent missed deadline after that adds another £200. Points expire after 24 months of on-time compliance. So right now there's real room to get the workflow right without a financial penalty attached to getting it wrong — which is exactly the window to build (or fix) the automation rather than wait.&lt;/p&gt;

&lt;h3&gt;
  
  
  One practical note on timing
&lt;/h3&gt;

&lt;p&gt;Submission is via API, and the sensible deadline to target isn't 23:59 on 7 November — it's a day or two earlier. API load spikes near statutory deadlines are a known pattern with HMRC's systems (MTD VAT saw the same thing), and "the portal was slow" is not a defence that holds up well in a compliance context.&lt;/p&gt;

&lt;p&gt;If you want the fuller walkthrough of the submission process and the HMRC-recognised software options, finance-journal.co.uk has a &lt;a href="https://finance-journal.co.uk/mtd-itsa-quarterly-updates-how-to-submit" rel="noopener noreferrer"&gt;detailed guide to the MTD ITSA quarterly submission process&lt;/a&gt; that covers the mechanics end to end.&lt;/p&gt;

</description>
      <category>uk</category>
      <category>tax</category>
      <category>freelance</category>
      <category>api</category>
    </item>
  </channel>
</rss>
