<?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: Anthony Leignel</title>
    <description>The latest articles on DEV Community by Anthony Leignel (@palks_studio).</description>
    <link>https://dev.to/palks_studio</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%2F3772766%2F42db137c-2300-4d6b-80ff-8d0acf68f56a.jpg</url>
      <title>DEV Community: Anthony Leignel</title>
      <link>https://dev.to/palks_studio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/palks_studio"/>
    <language>en</language>
    <item>
      <title>How Many Obsolete Dependencies Are You Using Without Knowing It? An Open Source Checker</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Sun, 16 Aug 2026 15:50:57 +0000</pubDate>
      <link>https://dev.to/palks_studio/how-many-obsolete-dependencies-are-you-using-without-knowing-it-an-open-source-checker-4nnl</link>
      <guid>https://dev.to/palks_studio/how-many-obsolete-dependencies-are-you-using-without-knowing-it-an-open-source-checker-4nnl</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%2F0cyneqyxe853med87awr.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%2F0cyneqyxe853med87awr.png" alt="Dependency Security Checker open source project" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Security also starts with what we install
&lt;/h2&gt;

&lt;p&gt;We install Python packages, VS Code extensions, tools that help us save time, test an idea, or simply improve our development environment.&lt;/p&gt;

&lt;p&gt;Then we move on.&lt;/p&gt;

&lt;p&gt;A few months or years later, some of those projects have been deprecated, archived, renamed, or are simply no longer maintained.&lt;/p&gt;

&lt;p&gt;And they are still installed.&lt;/p&gt;

&lt;p&gt;That is exactly the problem I wanted to address with &lt;strong&gt;Dependency Security Checker&lt;/strong&gt;, an open source tool that locally scans installed Python packages and VS Code extensions, then compares them against documented alert databases.&lt;/p&gt;

&lt;p&gt;The project is open source and available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Palks-Studio/dependency-security-checker" rel="noopener noreferrer"&gt;Dependency Security Checker, GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  An alert before a vulnerability becomes your problem
&lt;/h3&gt;

&lt;p&gt;The goal is not to claim that a dependency is vulnerable simply because it is no longer maintained.&lt;/p&gt;

&lt;p&gt;That would be incorrect.&lt;/p&gt;

&lt;p&gt;The goal is much simpler: &lt;strong&gt;to warn you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If an installed package has officially been deprecated, you should be able to know about it.&lt;/p&gt;

&lt;p&gt;If a VS Code extension you still use has been archived, you should be able to know that too.&lt;/p&gt;

&lt;p&gt;And if a project has been renamed or its maintainers now recommend an alternative, that information should be easy to find.&lt;/p&gt;

&lt;h3&gt;
  
  
  A tool that does not make decisions for you
&lt;/h3&gt;

&lt;p&gt;Dependency Security Checker does not uninstall anything, disable anything, update anything, or modify your projects.&lt;/p&gt;

&lt;p&gt;It scans, detects, and displays the available information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[!] package-name 1.0.0
    Status / Statut: DEPRECATED / DÉPRÉCIÉ

    Reason:
    ...

    Raison :
    ...

    Suggested replacement / Remplacement suggéré: ...

    Source: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, the decision is yours.&lt;/p&gt;

&lt;p&gt;You can check the source, understand why the project was flagged, review how you use it, and decide whether it should be kept, replaced, updated, or removed.&lt;/p&gt;

&lt;p&gt;This approach matters because a security-related tool should not create another problem by automatically modifying an environment it does not fully understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deprecated does not mean vulnerable
&lt;/h3&gt;

&lt;p&gt;This is probably the most important distinction behind the project.&lt;/p&gt;

&lt;p&gt;An abandoned dependency is not automatically vulnerable.&lt;/p&gt;

&lt;p&gt;Likewise, the absence of an alert does not mean a dependency is secure or free from vulnerabilities.&lt;/p&gt;

&lt;p&gt;But when a project is no longer maintained, one question becomes legitimate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who will fix the next vulnerability if one is discovered?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where dependency maintenance becomes directly relevant to security.&lt;/p&gt;

&lt;p&gt;Vulnerabilities do not only come from the code we write ourselves, they can also exist in the different components we add to our environments and projects.&lt;/p&gt;

&lt;p&gt;With the number of packages, libraries, extensions, and development tools a modern environment can accumulate, it becomes increasingly difficult to remember exactly what is still installed and whether every component is still maintained.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is it open source?
&lt;/h3&gt;

&lt;p&gt;Because when it comes to security, I prefer being able to see what a tool actually does.&lt;/p&gt;

&lt;p&gt;Dependency Security Checker runs locally, its alert databases are visible, its sources can be checked, and its behavior can be inspected directly in the code.&lt;/p&gt;

&lt;p&gt;It does not need to send your list of packages or VS Code extensions to a remote service to perform the scan.&lt;/p&gt;

&lt;p&gt;Today, with software failures, compromised dependencies, abandoned projects, and supply-chain security issues becoming increasingly difficult to ignore, security should not be treated as something we think about only after a problem occurs.&lt;/p&gt;

&lt;p&gt;Making the project open source also means something important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You do not have to simply trust the tool.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can inspect it.&lt;/p&gt;

&lt;p&gt;You can verify its data.&lt;/p&gt;

&lt;p&gt;You can contribute.&lt;/p&gt;

&lt;p&gt;You can report incorrect information or submit another officially deprecated or abandoned dependency with a public source.&lt;/p&gt;

&lt;h3&gt;
  
  
  You do not need to be a developer to understand the result
&lt;/h3&gt;

&lt;p&gt;I also wanted to avoid building something that only makes sense to someone who spends every day working in a terminal.&lt;/p&gt;

&lt;p&gt;A developer can use it as a quick check of their environment, but a less technical user can also run the checker and understand the essential result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No known alert found&lt;/strong&gt;, nothing installed matched the current database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An alert appears&lt;/strong&gt;, something deserves your attention.&lt;/p&gt;

&lt;p&gt;Then you get the status, the reason, a suggested replacement when one exists, and most importantly, the source.&lt;/p&gt;

&lt;p&gt;No mysterious score.&lt;/p&gt;

&lt;p&gt;No invented risk level.&lt;/p&gt;

&lt;p&gt;No automatic decision.&lt;/p&gt;

&lt;p&gt;The information remains understandable.&lt;/p&gt;

&lt;h3&gt;
  
  
  A database designed to evolve
&lt;/h3&gt;

&lt;p&gt;The first version already contains hundreds of documented alerts across Python packages and VS Code extensions.&lt;/p&gt;

&lt;p&gt;But the database is not intended to pretend it is exhaustive.&lt;/p&gt;

&lt;p&gt;New projects will be abandoned.&lt;/p&gt;

&lt;p&gt;Others will be archived.&lt;/p&gt;

&lt;p&gt;Some libraries will be renamed or replaced.&lt;/p&gt;

&lt;p&gt;The project can therefore evolve over time with one simple principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An alert should be backed by a source.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The objective is not to build the biggest list possible, but to maintain a database reliable enough for an alert to actually mean something when it appears.&lt;/p&gt;

&lt;h3&gt;
  
  
  A few seconds to look at what we are using
&lt;/h3&gt;

&lt;p&gt;Software security can become extremely complex.&lt;/p&gt;

&lt;p&gt;Here, the principle is intentionally simple.&lt;/p&gt;

&lt;p&gt;You run the checker.&lt;/p&gt;

&lt;p&gt;It looks at what is installed.&lt;/p&gt;

&lt;p&gt;It compares it against its local database.&lt;/p&gt;

&lt;p&gt;If it finds something, it tells you.&lt;/p&gt;

&lt;p&gt;Then you investigate.&lt;/p&gt;

&lt;p&gt;Because before deciding whether a dependency should still be part of your environment, &lt;strong&gt;you first need to know that it deserves your attention.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Palks-Studio/dependency-security-checker" rel="noopener noreferrer"&gt;Dependency Security Checker, GitHub&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>opensource</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Comparing PDF and XML Data Locally with Python, Open Source</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Sat, 15 Aug 2026 20:40:01 +0000</pubDate>
      <link>https://dev.to/palks_studio/comparing-pdf-and-xml-data-locally-with-python-502n</link>
      <guid>https://dev.to/palks_studio/comparing-pdf-and-xml-data-locally-with-python-502n</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%2Fwzsjl5bppxsw67tbrtd0.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%2Fwzsjl5bppxsw67tbrtd0.png" alt="PDF / XML Comparator, an open source tool for comparing PDF and XML data locally"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A lightweight open source tool for detecting differences between two representations of the same document
&lt;/h2&gt;

&lt;p&gt;A PDF and an XML file can represent the same document while containing different information.&lt;/p&gt;

&lt;p&gt;A value may have been modified in one representation, a reference may be missing, a number may use a different format, or some information may simply exist in one file but not the other.&lt;/p&gt;

&lt;p&gt;When both files become part of the same workflow, checking them manually can quickly become tedious.&lt;/p&gt;

&lt;p&gt;This is why I built &lt;strong&gt;PDF / XML Comparator&lt;/strong&gt;, a small open source Python tool designed to perform a first comparison locally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Palks-Studio/pdf-xml-comparator" rel="noopener noreferrer"&gt;View PDF / XML Comparator on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;PDF and XML serve very different purposes.&lt;/p&gt;

&lt;p&gt;The PDF provides a human-readable representation of a document, while XML provides structured data that can be processed by software.&lt;/p&gt;

&lt;p&gt;Even when both representations originate from the same process, having two files does not automatically guarantee that every value represented in one can also be found in the other.&lt;/p&gt;

&lt;p&gt;For example, you may encounter situations where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a reference differs between the PDF and XML&lt;/li&gt;
&lt;li&gt;an amount was changed in one representation&lt;/li&gt;
&lt;li&gt;a value exists only in the XML&lt;/li&gt;
&lt;li&gt;information visible in the PDF cannot be found in the XML&lt;/li&gt;
&lt;li&gt;equivalent values use different formatting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Opening both files and searching for every value manually works for a single document, but it quickly becomes inefficient when repeated.&lt;/p&gt;

&lt;h2&gt;
  
  
  A deliberately simple approach
&lt;/h2&gt;

&lt;p&gt;PDF / XML Comparator does not try to understand the business meaning of every field.&lt;/p&gt;

&lt;p&gt;Instead, it performs a bidirectional comparison.&lt;/p&gt;

&lt;p&gt;The process can be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PDF ──► Text extraction ──► Search values in XML
                              │
                              ▼
                         Differences
                              ▲
                              │
XML ──► Data extraction ──► Search values in PDF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Compare Two PDF Files Locally with Python, Open Source</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Wed, 12 Aug 2026 19:41:42 +0000</pubDate>
      <link>https://dev.to/palks_studio/compare-two-pdf-files-locally-with-python-43il</link>
      <guid>https://dev.to/palks_studio/compare-two-pdf-files-locally-with-python-43il</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%2F3zhqds9qgduded8vuk03.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%2F3zhqds9qgduded8vuk03.png" alt="PDF Comparator" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A lightweight open-source tool for detecting textual differences between PDFs
&lt;/h2&gt;

&lt;p&gt;Comparing two versions of a PDF should not necessarily require uploading documents to an online service.&lt;/p&gt;

&lt;p&gt;This is especially true when working with internal documentation, reports, invoices, technical files, or documents that simply should not leave your machine.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;PDF Comparator&lt;/strong&gt;, a small open-source Python tool designed for exactly that purpose.&lt;/p&gt;

&lt;p&gt;It compares the textual content of two PDF files locally and shows what changed between them.&lt;/p&gt;

&lt;p&gt;No AI, no external API, no account, and no document upload.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The principle is intentionally simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PDF A → Text extraction
&lt;/li&gt;
&lt;li&gt;PDF B → Text extraction
→ Lightweight normalization
→ Comparison
→ Differences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first PDF is considered the reference version, while the second is the new version.&lt;/p&gt;

&lt;p&gt;The tool extracts text from all pages, performs lightweight normalization, then detects added and removed lines.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- old.pdf
+++ new.pdf

-Address: 12 Example Street
+Address: 24 Example Street

-Total: €1,200
+Total: €1,350
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of manually checking two documents line by line, the differences are immediately visible in the terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;p&gt;The project only requires Python 3 and pypdf.&lt;/p&gt;

&lt;p&gt;Clone the repository and install the dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/Palks-Studio/pdf-comparator
cd pdf-comparator

python -m pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then compare two PDFs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python pdf-compare.py old.pdf new.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no textual difference is found, the tool simply reports that no difference was detected.&lt;/p&gt;

&lt;p&gt;Otherwise, the differences are displayed directly in the terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local by design
&lt;/h2&gt;

&lt;p&gt;One of the main reasons I built this tool was to keep the workflow simple and local.&lt;/p&gt;

&lt;p&gt;The PDFs are processed directly on your machine.&lt;/p&gt;

&lt;p&gt;There is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no file upload&lt;/li&gt;
&lt;li&gt;no remote server&lt;/li&gt;
&lt;li&gt;no external API&lt;/li&gt;
&lt;li&gt;no AI processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For documents containing internal or sensitive information, avoiding an unnecessary external transfer can be just as important as the comparison itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it can be useful for
&lt;/h2&gt;

&lt;p&gt;PDF Comparator can help when reviewing different versions of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;technical documentation&lt;/li&gt;
&lt;li&gt;reports&lt;/li&gt;
&lt;li&gt;invoices&lt;/li&gt;
&lt;li&gt;specifications&lt;/li&gt;
&lt;li&gt;administrative documents&lt;/li&gt;
&lt;li&gt;generated PDFs&lt;/li&gt;
&lt;li&gt;archived documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It can also be useful during development when a system generates PDFs and you want a quick way to check whether textual content changed between two outputs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does not do
&lt;/h2&gt;

&lt;p&gt;PDF Comparator is deliberately focused on &lt;strong&gt;textual differences&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It does not compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;images&lt;/li&gt;
&lt;li&gt;fonts&lt;/li&gt;
&lt;li&gt;colors&lt;/li&gt;
&lt;li&gt;graphical layout&lt;/li&gt;
&lt;li&gt;visual rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also does not interpret the meaning of a change.&lt;/p&gt;

&lt;p&gt;If a value changes from &lt;code&gt;1200&lt;/code&gt; to &lt;code&gt;1350&lt;/code&gt;, the tool detects the difference, but it does not try to determine whether that change is correct.&lt;/p&gt;

&lt;p&gt;The quality of the comparison also depends on the text layer available inside the PDF. A scanned document containing only images cannot be reliably compared without OCR.&lt;/p&gt;

&lt;p&gt;Keeping this distinction explicit makes the tool predictable: it does one job and stays focused on it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;PDF Comparator is available as an open-source project under the MIT License.&lt;/p&gt;

&lt;p&gt;The repository contains the Python script, requirements, documentation in English and French, and installation instructions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Palks-Studio/pdf-comparator" rel="noopener noreferrer"&gt;View PDF Comparator on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contributions, feedback, issues and improvements are welcome.&lt;/p&gt;

&lt;p&gt;Sometimes a useful tool does not need a platform, an account, or another service in the middle.&lt;/p&gt;

&lt;p&gt;Two files, one local script, and the differences.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>What if we stopped explaining technology only through text?</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Sun, 09 Aug 2026 19:16:07 +0000</pubDate>
      <link>https://dev.to/palks_studio/what-if-we-stopped-explaining-technology-only-through-text-37lh</link>
      <guid>https://dev.to/palks_studio/what-if-we-stopped-explaining-technology-only-through-text-37lh</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%2Fpzv5f1efauahdryzjon9.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%2Fpzv5f1efauahdryzjon9.png" alt="Interactive cookie experience interface" width="799" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  I created interactive experiences to show what really happens behind actions that seem simple
&lt;/h2&gt;

&lt;p&gt;A cookie accepted with one click, two systems connected through an API, a task to automate, user authentication, or even a Factur-X invoice.&lt;/p&gt;

&lt;p&gt;From the outside, many technical actions look simple.&lt;/p&gt;

&lt;p&gt;And that's perfectly normal.&lt;/p&gt;

&lt;p&gt;Users see the result, not necessarily everything that needs to happen behind the scenes for that result to work properly.&lt;/p&gt;

&lt;p&gt;Of course, these mechanisms can be explained through articles, documentation, or diagrams, but not everyone wants to read several pages to understand a topic, some simply don't have the time, while others understand things better when they can visually follow what is happening.&lt;/p&gt;

&lt;p&gt;That's why I started creating a series of interactive experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand in just a few clicks
&lt;/h2&gt;

&lt;p&gt;The principle is intentionally simple.&lt;/p&gt;

&lt;p&gt;Each experience starts with an action or idea that seems straightforward, then gradually reveals what is actually happening behind the scenes.&lt;/p&gt;

&lt;p&gt;Let's take automation.&lt;/p&gt;

&lt;p&gt;A request can start with something as simple as:&lt;/p&gt;

&lt;p&gt;"Could we automate this task?"&lt;/p&gt;

&lt;p&gt;For the person making the request, it really is just one task.&lt;/p&gt;

&lt;p&gt;But integrating it properly into a system may require analyzing the existing process, defining business rules, validating data, anticipating errors, securing execution, handling repeated runs, testing different scenarios, and making sure the system remains stable over time.&lt;/p&gt;

&lt;p&gt;The experience isn't designed to turn visitors into developers, it simply makes this part of the work visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Showing what the interface doesn't show
&lt;/h2&gt;

&lt;p&gt;That's the common thread throughout the collection.&lt;/p&gt;

&lt;p&gt;An API can make it look like two systems are simply exchanging data, yet behind a single request there may be authentication, validation, processing, response codes, errors, timeouts, and retries.&lt;/p&gt;

&lt;p&gt;A user login may look like nothing more than an email address and a password, while a properly built application needs to perform several checks before granting access.&lt;/p&gt;

&lt;p&gt;A Factur-X invoice looks like a PDF, but it also contains structured XML data, and both representations need to tell the same story.&lt;/p&gt;

&lt;p&gt;A cookie may seem insignificant, yet it can become the starting point of a much larger chain involving data collection and profiling.&lt;/p&gt;

&lt;p&gt;The goal isn't to make these topics seem artificially complicated or alarming, but to show that between a visible action and its result, there is often an entire system the user never sees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another way to talk about technology
&lt;/h2&gt;

&lt;p&gt;I like technical articles and documentation, and I publish those too, but I wanted to add a different format.&lt;/p&gt;

&lt;p&gt;Something you can explore quickly, without requiring any particular technical background, where a few clicks are enough to understand an idea.&lt;/p&gt;

&lt;p&gt;The experiences are therefore intentionally short, visual, and progressive.&lt;/p&gt;

&lt;p&gt;Today, the collection covers several areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;systems and architecture, with from-scratch development, APIs, and automation&lt;/li&gt;
&lt;li&gt;security and data, with application authentication, cookies, and personal data&lt;/li&gt;
&lt;li&gt;electronic invoicing, with Factur-X&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More experiences will be added to the collection, because there are still plenty of technical mechanisms worth making visible this way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore the interactive experiences
&lt;/h2&gt;

&lt;p&gt;If you'd rather experience something than read a long explanation, or if you simply want to discover what happens behind some of the mechanisms we use every day, the full collection is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/interactive-experiences" rel="noopener noreferrer"&gt;Explore the interactive experiences&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few clicks are enough, then it's up to you to decide how far you want to follow the journey.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>security</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Palks Studio Approach</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Tue, 04 Aug 2026 14:03:24 +0000</pubDate>
      <link>https://dev.to/palks_studio/-the-palks-studio-approach-300e</link>
      <guid>https://dev.to/palks_studio/-the-palks-studio-approach-300e</guid>
      <description>&lt;h2&gt;
  
  
  Why Palks Studio exists
&lt;/h2&gt;

&lt;p&gt;Palks Studio was created around a simple observation: many software projects become more complex than they need to be.&lt;/p&gt;

&lt;p&gt;New tools are added to solve problems introduced by previous tools. External services become permanent dependencies. Business data is scattered across multiple platforms. Over time, maintenance becomes more expensive than development itself.&lt;/p&gt;

&lt;p&gt;I chose a different approach.&lt;/p&gt;

&lt;p&gt;The goal is not to build software with the largest possible technology stack. The goal is to build systems that remain understandable, maintainable, and reliable years after they are deployed.&lt;/p&gt;

&lt;p&gt;Every project starts with the same question:&lt;/p&gt;

&lt;p&gt;How can this problem be solved with the fewest moving parts while remaining robust, secure, and easy to maintain?&lt;/p&gt;

&lt;p&gt;Sometimes that means using an existing library. Sometimes it means writing a component from scratch. The decision is driven by long-term stability rather than short-term convenience.&lt;/p&gt;

&lt;p&gt;This philosophy shapes every system developed at Palks Studio, from electronic invoicing and business automation to internal tools and backend infrastructures.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Palks-Studio" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/@Palks_Studio" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/in/palks-studio/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.malt.fr/profile/anthonyleignel" rel="noopener noreferrer"&gt;Malt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Simplicity is an engineering choice
&lt;/h2&gt;

&lt;p&gt;Simple software is often perceived as limited.&lt;/p&gt;

&lt;p&gt;In reality, simplicity is usually the result of deliberate engineering decisions.&lt;/p&gt;

&lt;p&gt;Every additional service, dependency, framework, or external platform increases the number of components that must be deployed, monitored, updated, secured, and maintained over time.&lt;/p&gt;

&lt;p&gt;Keeping an architecture simple is not about avoiding technology. It is about avoiding unnecessary complexity.&lt;/p&gt;

&lt;p&gt;At Palks Studio, each component must have a clear purpose. If a feature can be implemented without introducing another layer of abstraction or another external dependency, that option is considered first.&lt;/p&gt;

&lt;p&gt;The objective is not to build the smallest application possible. It is to build systems that remain understandable, predictable, and maintainable long after the initial development phase.&lt;/p&gt;

&lt;p&gt;A simple architecture is easier to document, easier to audit, easier to debug, and easier to evolve.&lt;/p&gt;

&lt;p&gt;Complexity should come from solving business problems, not from the technical stack itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Own the infrastructure
&lt;/h2&gt;

&lt;p&gt;Software should not require handing over control of a company's data or infrastructure.&lt;/p&gt;

&lt;p&gt;Whenever possible, the systems developed at Palks Studio are deployed directly on the client's own hosting, whether it is a shared server, a VPS, or dedicated infrastructure.&lt;/p&gt;

&lt;p&gt;This approach gives the client full ownership of their environment. Their data remains under their control, backups stay on their infrastructure, and the system continues to operate independently of any specific software vendor.&lt;/p&gt;

&lt;p&gt;It also simplifies long-term maintenance. There is no proprietary platform to migrate away from, no subscription required to keep the software running, and no dependency on a third-party service that could change its pricing, features, or availability.&lt;/p&gt;

&lt;p&gt;The objective is not to avoid cloud services at all costs. It is to ensure that the client remains in control of the software they rely on every day.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data belongs to the client
&lt;/h2&gt;

&lt;p&gt;Collecting data is easy. Deciding not to collect it requires a different mindset.&lt;/p&gt;

&lt;p&gt;At Palks Studio, systems are designed to keep only the information that is necessary for their intended purpose. If a piece of data has no operational value, it should not be stored.&lt;/p&gt;

&lt;p&gt;The same principle applies to the public website. It operates without advertising trackers, analytics scripts, or unnecessary cookies. Visitors are not profiled, and browsing does not generate data that serves no purpose.&lt;/p&gt;

&lt;p&gt;Keeping less data has practical benefits. It reduces storage requirements, simplifies compliance with privacy regulations, limits the impact of a potential data breach, and makes systems easier to understand and maintain.&lt;/p&gt;

&lt;p&gt;Data should help users achieve their goals. It should not become a product in itself.&lt;/p&gt;

&lt;p&gt;This principle guides every project developed at Palks Studio.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security starts with architecture
&lt;/h2&gt;

&lt;p&gt;Security is often associated with encryption, firewalls, or authentication.&lt;/p&gt;

&lt;p&gt;These are important, but they are only part of the picture.&lt;/p&gt;

&lt;p&gt;A system also becomes more secure by reducing what it exposes.&lt;/p&gt;

&lt;p&gt;Fewer external services mean fewer communication channels.&lt;/p&gt;

&lt;p&gt;Fewer dependencies mean fewer components that require security updates.&lt;/p&gt;

&lt;p&gt;Fewer stored data mean less information that could be compromised.&lt;/p&gt;

&lt;p&gt;Whenever possible, systems are designed with a clear separation between public and private components. Configuration files, business logic, and sensitive data remain outside the public web root, while only the elements intended to be accessed by users are exposed.&lt;/p&gt;

&lt;p&gt;The objective is not to eliminate every risk. It is to reduce the attack surface before the software is even deployed.&lt;/p&gt;

&lt;p&gt;Good security is not only something added to a system.&lt;/p&gt;

&lt;p&gt;It is something built into its architecture from the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Business rules before automation
&lt;/h2&gt;

&lt;p&gt;Automation is often presented as the solution.&lt;/p&gt;

&lt;p&gt;In reality, it only amplifies the quality of the process it executes.&lt;/p&gt;

&lt;p&gt;Automating a poorly designed workflow does not make it better. It simply allows the same mistakes to happen faster and more consistently.&lt;/p&gt;

&lt;p&gt;Before introducing automation, the business rules must be clearly defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What should happen?
&lt;/li&gt;
&lt;li&gt;Under which conditions?
&lt;/li&gt;
&lt;li&gt;What data is required?
&lt;/li&gt;
&lt;li&gt;What makes a process valid?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only when these questions have clear answers does automation become valuable.&lt;/p&gt;

&lt;p&gt;This principle applies to every project developed at Palks Studio. Whether generating a Factur-X invoice, processing job applications, or orchestrating a business workflow, the objective is always the same: understand the business logic first, then automate its execution.&lt;/p&gt;

&lt;p&gt;Automation should execute decisions.&lt;/p&gt;

&lt;p&gt;It should not replace them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Systems instead of tools
&lt;/h2&gt;

&lt;p&gt;A tool solves a task.&lt;/p&gt;

&lt;p&gt;A system supports a workflow.&lt;/p&gt;

&lt;p&gt;This distinction shapes every project developed at Palks Studio.&lt;/p&gt;

&lt;p&gt;A billing system is more than invoice generation. It also manages quotations, signatures, payments, structured archives, compliance rules, and future interoperability.&lt;/p&gt;

&lt;p&gt;A recruitment system is more than a form. It evaluates candidates, applies business rules, ranks profiles, manages campaigns, and keeps the recruitment process consistent.&lt;/p&gt;

&lt;p&gt;An automation system is more than a script. It validates data, executes predefined workflows, handles errors, produces traceable outputs, and remains predictable over time.&lt;/p&gt;

&lt;p&gt;The objective is never to automate a single action in isolation.&lt;/p&gt;

&lt;p&gt;It is to build systems that remain coherent as they grow.&lt;/p&gt;

&lt;p&gt;Good software is not defined by the number of features it contains.&lt;/p&gt;

&lt;p&gt;It is defined by the reliability of the workflow it supports.&lt;/p&gt;




&lt;h2&gt;
  
  
  A philosophy that guides every project
&lt;/h2&gt;

&lt;p&gt;Technologies evolve.&lt;/p&gt;

&lt;p&gt;Libraries are replaced.&lt;/p&gt;

&lt;p&gt;Standards change.&lt;/p&gt;

&lt;p&gt;Business requirements grow.&lt;/p&gt;

&lt;p&gt;The principles behind a well-designed system should remain the same.&lt;/p&gt;

&lt;p&gt;Build only what is necessary.&lt;/p&gt;

&lt;p&gt;Keep the architecture understandable.&lt;/p&gt;

&lt;p&gt;Reduce unnecessary dependencies.&lt;/p&gt;

&lt;p&gt;Protect the client's data.&lt;/p&gt;

&lt;p&gt;Design business rules before automating them.&lt;/p&gt;

&lt;p&gt;Create systems that can still be understood, maintained, and trusted years after they are deployed.&lt;/p&gt;

&lt;p&gt;Every project developed at Palks Studio follows these principles.&lt;/p&gt;

&lt;p&gt;They are not constraints.&lt;/p&gt;

&lt;p&gt;They are the foundation.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>I Built a Local VS Code Environment That Puts Me Back in Control</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Wed, 01 Jul 2026 17:27:54 +0000</pubDate>
      <link>https://dev.to/palks_studio/i-built-a-local-vs-code-environment-that-saves-me-hours-on-every-project-lpc</link>
      <guid>https://dev.to/palks_studio/i-built-a-local-vs-code-environment-that-saves-me-hours-on-every-project-lpc</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%2F0tuju5l9nxx224fkdqef.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%2F0tuju5l9nxx224fkdqef.png" alt="Before and after comparison showing automatic code reindentation and whitespace cleanup applied to the same source file." width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;For years, I kept adding new tools to my development environment because every extension, formatter, or utility promised to make development faster. At first it worked, but over time my workflow became harder to understand. Files were modified automatically, formatting changed from one project to another, and some actions happened in the background without me even noticing.&lt;/p&gt;

&lt;p&gt;The problem wasn't Visual Studio Code, and it wasn't the extensions themselves. The real problem was that I had gradually stopped controlling my own development environment.&lt;/p&gt;

&lt;p&gt;Instead of installing even more tools, I started removing them. I replaced hidden automation with explicit actions and built a lightweight local environment around a simple principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nothing should modify my code unless I decide it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over time, this approach evolved into what is now my VS Code Environment Pack. Rather than trying to automate everything, it provides a collection of local tools that help me create projects, maintain codebases, generate documentation, keep timestamped backups, and automate repetitive tasks while keeping every action predictable and under my control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://palks-studio.com/en/vscode-environment-pack" rel="noopener noreferrer"&gt;View Resource&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=NZgXd_sHEy4&amp;amp;list=PLR1GDrpaWT2k" rel="noopener noreferrer"&gt;YouTube playlist&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Palks-Studio/vs-code-environment-pack" rel="noopener noreferrer"&gt;View the GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Every Project Starts the Same Way
&lt;/h2&gt;

&lt;p&gt;One of the first repetitive tasks in any project has nothing to do with writing code.&lt;/p&gt;

&lt;p&gt;Creating folders.&lt;/p&gt;

&lt;p&gt;Creating empty files.&lt;/p&gt;

&lt;p&gt;Reproducing the same directory structure again and again.&lt;/p&gt;

&lt;p&gt;It doesn't take long, but after dozens of projects it becomes unnecessary work.&lt;/p&gt;

&lt;p&gt;Instead of creating everything manually, I describe the project structure once in a simple &lt;code&gt;structure.txt&lt;/code&gt; file. Running a single task generates the complete hierarchy, including every folder and file exactly where it belongs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_project

    README.md

    app

        app.py

    static

        css

            style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't to generate code.&lt;/p&gt;

&lt;p&gt;It's to start every project from a clean, consistent structure, so I can focus on solving the actual problem instead of rebuilding the same foundation every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Documentation Should Never Be Manual
&lt;/h2&gt;

&lt;p&gt;Understanding a project often starts with a simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How is it organized?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a codebase grows, answering that question manually becomes surprisingly time-consuming. Taking screenshots, writing folder structures, or listing files by hand quickly becomes outdated.&lt;/p&gt;

&lt;p&gt;That's why I built &lt;code&gt;tree.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With a single task, it scans the entire project and generates a &lt;code&gt;PROJECT_TREE.txt&lt;/code&gt; file representing the complete directory structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_project/
├── app
│   ├── app.py
│   └── controllers
├── static
│   ├── css
│   └── js
├── README.md
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use it to document projects, share their architecture, review existing codebases, and quickly understand how a project is organized.&lt;/p&gt;

&lt;p&gt;Like every other tool in this environment, it performs one task, does it locally, and produces a result that is easy to read, reuse, and version.&lt;/p&gt;




&lt;h2&gt;
  
  
  Every Change Is Intentional
&lt;/h2&gt;

&lt;p&gt;One of the principles behind this environment is that no script should modify more files than necessary.&lt;/p&gt;

&lt;p&gt;That's why most maintenance tools can run in three different modes.&lt;/p&gt;

&lt;p&gt;Sometimes I want to clean an entire project before committing it.&lt;/p&gt;

&lt;p&gt;Sometimes I only want to work on the file I'm editing.&lt;/p&gt;

&lt;p&gt;And sometimes I need to target a small selection of files without touching anything else.&lt;/p&gt;

&lt;p&gt;Instead of forcing a single workflow, the environment adapts to the situation.&lt;/p&gt;

&lt;p&gt;The same approach applies to cleaning whitespace, converting line endings, and other maintenance tasks. Every operation can be executed on the whole project, the active file, or a custom selection.&lt;/p&gt;

&lt;p&gt;Nothing runs automatically in the background.&lt;/p&gt;

&lt;p&gt;Nothing changes unless I ask for it.&lt;/p&gt;

&lt;p&gt;That level of control makes maintenance safer, especially on large projects where a single unintended modification can affect hundreds of files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Every Save Creates a Safety Net
&lt;/h2&gt;

&lt;p&gt;Making changes is part of development.&lt;/p&gt;

&lt;p&gt;Deleting the wrong block, trying a different implementation, or restructuring a file happens every day. Most of the time, version control is enough. Sometimes, it isn't.&lt;/p&gt;

&lt;p&gt;I wanted something much simpler.&lt;/p&gt;

&lt;p&gt;Every time I save a file, a timestamped copy is created automatically inside a local backup directory. Nothing is uploaded, nothing is synchronized, and nothing depends on an external extension or service.&lt;/p&gt;

&lt;p&gt;If I realize a mistake a few minutes later, I don't need to search through Git commits or recover a deleted file. The previous version is already there.&lt;/p&gt;

&lt;p&gt;More importantly, this changes the way I work.&lt;/p&gt;

&lt;p&gt;I can experiment more freely because I know every saved version still exists. Refactoring becomes less stressful, trying a new approach becomes easier, and recovering from a mistake takes seconds instead of minutes.&lt;/p&gt;

&lt;p&gt;The backup system isn't just about protecting files.&lt;/p&gt;

&lt;p&gt;It's about giving me the confidence to change them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Small Tools, One Responsibility
&lt;/h2&gt;

&lt;p&gt;One design decision guided the entire project: every tool should solve one problem, and solve it well.&lt;/p&gt;

&lt;p&gt;Instead of building one large application responsible for formatting, backups, project generation, documentation, and file maintenance, I chose to keep every feature independent.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create.py&lt;/code&gt; creates projects.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tree.py&lt;/code&gt; documents them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;clean.py&lt;/code&gt; removes unnecessary whitespace.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;space.py&lt;/code&gt; reports formatting issues without modifying anything.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;convert.py&lt;/code&gt; normalizes line endings.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;backup.py&lt;/code&gt; keeps timestamped copies of every saved file.&lt;/p&gt;

&lt;p&gt;Each tool can evolve independently, remain easy to understand, and be reused in other projects without bringing along features that aren't needed.&lt;/p&gt;

&lt;p&gt;The result isn't just easier to maintain.&lt;/p&gt;

&lt;p&gt;It's easier to trust.&lt;/p&gt;

&lt;p&gt;When a tool has one responsibility, you always know why you're running it and exactly what it will do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This project didn't start as a VS Code pack.&lt;/p&gt;

&lt;p&gt;It started with a simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can I build an environment that stays predictable, even as my projects become larger and more complex?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over time, the answer became a collection of small local tools designed to solve everyday problems without introducing unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Creating projects.&lt;/p&gt;

&lt;p&gt;Documenting architectures.&lt;/p&gt;

&lt;p&gt;Maintaining codebases.&lt;/p&gt;

&lt;p&gt;Keeping local backups.&lt;/p&gt;

&lt;p&gt;Cleaning and formatting files.&lt;/p&gt;

&lt;p&gt;Every tool has a single responsibility, every action is explicit, and every modification remains under my control.&lt;/p&gt;

&lt;p&gt;Today, this environment is part of every project I build.&lt;/p&gt;

&lt;p&gt;Not because it automates everything, but because it automates the right things while letting me decide when and how they happen.&lt;/p&gt;

&lt;p&gt;For me, that's what a good development environment should do.&lt;/p&gt;

&lt;p&gt;It shouldn't write code for the developer.&lt;/p&gt;

&lt;p&gt;It should help the developer write better code with confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Further
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/content" rel="noopener noreferrer"&gt;Explore more technical articles&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://palks-studio.com/en/technical-notes" rel="noopener noreferrer"&gt;Read more technical notes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>webdev</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built a Local Data Collection System in Python</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Thu, 04 Jun 2026 17:13:36 +0000</pubDate>
      <link>https://dev.to/palks_studio/i-built-a-local-data-collection-system-in-python-3k6p</link>
      <guid>https://dev.to/palks_studio/i-built-a-local-data-collection-system-in-python-3k6p</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.amazonaws.com%2Fuploads%2Farticles%2Fikdp3uzo5b1idyprjcnc.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.amazonaws.com%2Fuploads%2Farticles%2Fikdp3uzo5b1idyprjcnc.png" alt="Results interface showing collected prospects with columns for name, website, email, phone, city, country, and a checkbox to mark contacted leads; includes filters and export options" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;Most data collection and lead generation tools are delivered as SaaS products.&lt;/p&gt;

&lt;p&gt;You create an account, subscribe, send your data to a third-party platform and keep paying every month to continue using the tool.&lt;/p&gt;

&lt;p&gt;I wanted a different approach.&lt;/p&gt;

&lt;p&gt;I built a fully local data collection system that runs on the user's machine, without subscriptions, without paid APIs and without relying on external platforms.&lt;/p&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create searches
&lt;/li&gt;
&lt;li&gt;Collect data from multiple sources
&lt;/li&gt;
&lt;li&gt;Clean and validate the results
&lt;/li&gt;
&lt;li&gt;Export usable data
&lt;/li&gt;
&lt;li&gt;Keep everything under the user's control&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://palks-studio.com/en/data-collection" rel="noopener noreferrer"&gt;View System&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=W12Njqldd7s" rel="noopener noreferrer"&gt;Watch the Complete Demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Palks-Studio/data-collection-system" rel="noopener noreferrer"&gt;View the GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What the System Does
&lt;/h2&gt;

&lt;p&gt;The application allows users to create and manage searches from a web interface.&lt;/p&gt;

&lt;p&gt;For each search, users can define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A search name
&lt;/li&gt;
&lt;li&gt;A keyword
&lt;/li&gt;
&lt;li&gt;A city
&lt;/li&gt;
&lt;li&gt;A country
&lt;/li&gt;
&lt;li&gt;The search engines to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system currently supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DuckDuckGo
&lt;/li&gt;
&lt;li&gt;Bing
&lt;/li&gt;
&lt;li&gt;Qwant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once a search is executed, the collection engine starts gathering information from the selected sources.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data Processing Pipeline
&lt;/h2&gt;

&lt;p&gt;The system follows a structured workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Load Configuration
↓
Initialize Environment
↓
Load Searches
↓
Run Collectors
↓
Clean Data
↓
Normalize Data
↓
Validate Records
↓
Remove Duplicates
↓
Generate Exports
↓
Save Results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each component has a single responsibility.&lt;/p&gt;

&lt;p&gt;Collectors collect data.&lt;/p&gt;

&lt;p&gt;Processors clean and validate it.&lt;/p&gt;

&lt;p&gt;Exporters generate output files.&lt;/p&gt;

&lt;p&gt;The main engine orchestrates the workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Extracting Business Information
&lt;/h2&gt;

&lt;p&gt;The system doesn't stop at search engine results.&lt;/p&gt;

&lt;p&gt;When a website is discovered, the application can visit the site and extract useful information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website URL
&lt;/li&gt;
&lt;li&gt;Email addresses
&lt;/li&gt;
&lt;li&gt;Phone numbers
&lt;/li&gt;
&lt;li&gt;Company name
&lt;/li&gt;
&lt;li&gt;Location information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The collected data is then normalized and validated before being added to the final dataset.&lt;/p&gt;




&lt;h2&gt;
  
  
  Managing Contacted Leads
&lt;/h2&gt;

&lt;p&gt;One feature I wanted from the beginning was lead tracking.&lt;/p&gt;

&lt;p&gt;Users can mark prospects as contacted directly from the interface.&lt;/p&gt;

&lt;p&gt;The information is stored locally and remains available after closing the application.&lt;/p&gt;

&lt;p&gt;This makes it easy to distinguish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New prospects
&lt;/li&gt;
&lt;li&gt;Already contacted prospects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without relying on an external CRM.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exporting Data
&lt;/h2&gt;

&lt;p&gt;Once processing is complete, results can be exported as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSV
&lt;/li&gt;
&lt;li&gt;JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exported files are ready to be imported into other systems or used for further analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local First
&lt;/h2&gt;

&lt;p&gt;One of the main design goals was independence.&lt;/p&gt;

&lt;p&gt;The system runs locally.&lt;/p&gt;

&lt;p&gt;There is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No SaaS
&lt;/li&gt;
&lt;li&gt;No subscription
&lt;/li&gt;
&lt;li&gt;No third-party account
&lt;/li&gt;
&lt;li&gt;No paid API dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The user owns the software and the collected data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical Stack
&lt;/h2&gt;

&lt;p&gt;The project is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python
&lt;/li&gt;
&lt;li&gt;Flask
&lt;/li&gt;
&lt;li&gt;Playwright
&lt;/li&gt;
&lt;li&gt;BeautifulSoup
&lt;/li&gt;
&lt;li&gt;Requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interface is served locally through Flask and can be accessed from a browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This project started as a simple data collection tool and gradually evolved into a complete workflow capable of collecting, processing and exporting structured business data.&lt;/p&gt;

&lt;p&gt;Building it locally introduced some interesting challenges around browser automation, data normalization, validation and architecture design.&lt;/p&gt;

&lt;p&gt;The result is a modular system that can be extended with new collectors, processors and export formats without changing the overall architecture.&lt;/p&gt;

&lt;p&gt;For me, the most important aspect remains simple:&lt;/p&gt;

&lt;p&gt;The software belongs to the user and the data never has to leave the machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Further
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/content" rel="noopener noreferrer"&gt;Explore more technical articles&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://palks-studio.com/en/technical-notes" rel="noopener noreferrer"&gt;Read more technical notes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built a Full Recruitment System in PHP: No Database, No SaaS, No Subscription</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Fri, 15 May 2026 19:53:59 +0000</pubDate>
      <link>https://dev.to/palks_studio/i-built-a-full-recruitment-system-in-php-no-database-no-saas-no-subscription-ff1</link>
      <guid>https://dev.to/palks_studio/i-built-a-full-recruitment-system-in-php-no-database-no-saas-no-subscription-ff1</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.amazonaws.com%2Fuploads%2Farticles%2Fayyv322ema09dkzlmf73.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.amazonaws.com%2Fuploads%2Farticles%2Fayyv322ema09dkzlmf73.png" alt="CANDIDATE_FLOW recruiter dashboard — applications ranked by matching score" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic candidate scoring, recruiter dashboard, multi-campaign management. Deployed on any standard Apache hosting.
&lt;/h2&gt;

&lt;p&gt;After building a complete invoicing system the same way, I applied the same philosophy to recruitment: no SaaS dependency, no monthly subscription, no data leaving the client's server.&lt;/p&gt;

&lt;p&gt;The result is &lt;strong&gt;CANDIDATE_SYSTEM&lt;/strong&gt;, a self-hosted recruitment engine that automatically scores applicants against a job profile, ranks them by matching score, and lets the recruiter focus on the top profiles instead of reading through dozens of CVs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://palks-studio.com/en/recruitment-without-saas" rel="noopener noreferrer"&gt;View System&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=TNNa5O3VrhA&amp;amp;list=PLP79X9pb3KlU" rel="noopener noreferrer"&gt;Watch the Complete Demo Playlist&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Palks-Studio/candidat-system" rel="noopener noreferrer"&gt;View the GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem With Existing Tools
&lt;/h2&gt;

&lt;p&gt;Most recruitment platforms follow the same model: you pay monthly, your data sits on their servers, and the day you stop paying, everything disappears.&lt;/p&gt;

&lt;p&gt;For small companies, DSIs, and independent recruiters, this makes no sense. They don't need a 300€/month SaaS. They need a simple, reliable tool that works on their own hosting.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The system is split into two parts:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public&lt;/strong&gt;: the application form (&lt;code&gt;/candidat/&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private&lt;/strong&gt;: configuration, scoring engine, candidate data (outside the webroot)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a candidate submits the form, the scoring engine calculates a final score based on three nested levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contribution = scores[answer] × (weight / 100) × (global_weight / 100)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Level 1&lt;/strong&gt;: &lt;code&gt;global_weight&lt;/code&gt;, how much a section counts in the final score.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Level 2&lt;/strong&gt;: &lt;code&gt;weight&lt;/code&gt;, how much a question counts within its section.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Level 3&lt;/strong&gt;: &lt;code&gt;scores&lt;/code&gt;, the raw value assigned to each possible answer (0 to 100).&lt;/p&gt;

&lt;p&gt;Every candidate gets a score on submission. The dashboard displays them ranked from highest to lowest.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Scoring Engine
&lt;/h2&gt;

&lt;p&gt;The scoring logic lives in a single &lt;code&gt;$coring&lt;/code&gt; class. It reads the campaign configuration files, questions with their weights and scores, and the job profile, then runs the calculation across all sections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$reponses&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$score_brut&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getScoreBrut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$reponses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$question&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
        &lt;span class="nv"&gt;$contribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$score_brut&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$question&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'weight'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$detail&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$question&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'section'&lt;/span&gt;&lt;span class="p"&gt;]][&lt;/span&gt;&lt;span class="s1"&gt;'score_section'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nv"&gt;$contribution&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$detail&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$section_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$section&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'contribution'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$section&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'score_section'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$section&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'global_weight'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$score_final&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nv"&gt;$section&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'contribution'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Apply penalties&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;profil&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'malus'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$question_id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$penalties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$penalties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$reponses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$question_id&lt;/span&gt;&lt;span class="p"&gt;]]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$malus&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nv"&gt;$penalties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$reponses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$question_id&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'score_final'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;max&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="nv"&gt;$score_final&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$malus&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="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tech stack question (&lt;code&gt;checkbox&lt;/code&gt; type) is scored differently, each selected technology is matched against the scores defined in the question itself. No hardcoded list, fully configurable from the admin interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-Campaign Architecture
&lt;/h2&gt;

&lt;p&gt;Each campaign is completely independent: its own questions, its own job profile, its own candidate data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;campaigns/
└── [slug]/
    ├── config/          → questions, scores, job profile
    └── data/            → applications, uploads, lock file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating a new campaign from the dashboard copies the templates, generates the folder structure, and redirects immediately to the new campaign. One click, ready to use.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;campaign.lock&lt;/code&gt; file controls whether the form is open or closed. The recruiter can close a campaign (which purges all candidate data and sends a closure email to every applicant), then reopen it later from the dashboard.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Admin Interface
&lt;/h2&gt;

&lt;p&gt;The recruiter configures everything from the admin panel, no file editing required:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Job profile&lt;/strong&gt;: position title, mission type, location, salary range
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Questions&lt;/strong&gt;: edit labels, answers, scores, section weights; add or delete questions
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Section weights&lt;/strong&gt;: real-time total indicator (must reach 100%)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Penalty rules&lt;/strong&gt;: attach negative scores to specific answers on specific questions
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settings&lt;/strong&gt;: sender name, email, site URL, dashboard password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The section weight total updates in real time as you type. If it doesn't add up to 100%, the indicator turns red.&lt;/p&gt;




&lt;h2&gt;
  
  
  No Database: Just JSON Files
&lt;/h2&gt;

&lt;p&gt;Every application is stored as a flat JSON file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20260514_143000_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-14 14:30:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score_final"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;74.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Good fit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score_detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"terrain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score_section"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;68.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"contribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"classic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score_section"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;75.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"contribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reponses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trigger_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No ORM, no migrations, no connection pooling. The dashboard loads all JSON files from the &lt;code&gt;applications/&lt;/code&gt; folder, sorts them by &lt;code&gt;score_final&lt;/code&gt;, and renders the list.&lt;/p&gt;

&lt;p&gt;It's fast enough for the volumes this tool is designed for, typically 20 to 200 applications per campaign.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;A few things worth noting:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSRF token&lt;/strong&gt; on the application form, generated per session and validated on submission
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict public/private separation&lt;/strong&gt;, no configuration file is accessible via the web
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate prevention&lt;/strong&gt;, one email per campaign, checked on every submission
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload validation&lt;/strong&gt;, PDF only, 5 MB max, with extension and MIME type verified
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP security headers&lt;/strong&gt; on every page (X-Frame-Options, X-Content-Type-Options, Referrer-Policy)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session required&lt;/strong&gt; for the dashboard, exports, application viewing, and campaign closure&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conditional Form Fields
&lt;/h2&gt;

&lt;p&gt;Some questions trigger additional fields depending on the answer. For example, answering "Yes" to "Do you have a portfolio?" reveals a required URL field. Answering "Yes" to "Any certifications?" triggers a PDF upload.&lt;/p&gt;

&lt;p&gt;The trigger configuration lives in the questions config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"portfolio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trigger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Oui"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"field_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"portfolio_url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"field_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"field_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Portfolio URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the frontend, a small JS handler watches radio inputs and toggles the conditional field visibility. On the backend, &lt;code&gt;handler.php&lt;/code&gt; only validates the conditional field if the triggering answer was given.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Gets Delivered
&lt;/h2&gt;

&lt;p&gt;When deployed for a client:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full system installed on their hosting
&lt;/li&gt;
&lt;li&gt;First campaign configured with their job profile
&lt;/li&gt;
&lt;li&gt;Admin interface ready to use
&lt;/li&gt;
&lt;li&gt;Technical documentation + user guide
&lt;/li&gt;
&lt;li&gt;Color customization included&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No recurring cost. No dependency on external infrastructure. One flat fee.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;PHP 8.x: no framework, no Composer
&lt;/li&gt;
&lt;li&gt;Apache + &lt;code&gt;.htaccess&lt;/code&gt;: URL rewriting, security headers, directory protection
&lt;/li&gt;
&lt;li&gt;Vanilla JS: form validation, clipboard API, accordion UI
&lt;/li&gt;
&lt;li&gt;JSON flat files, zero database&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Go Further
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/content" rel="noopener noreferrer"&gt;Explore more technical articles&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://palks-studio.com/en/technical-notes" rel="noopener noreferrer"&gt;Read more technical notes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>php</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I built a fully automated Factur-X EN16931 batch pipeline in PHP: no SaaS, no agent, just cron and strict business logic</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Wed, 13 May 2026 14:04:09 +0000</pubDate>
      <link>https://dev.to/palks_studio/i-built-a-fully-automated-factur-x-en16931-batch-pipeline-in-php-no-saas-no-agent-just-cron-and-c84</link>
      <guid>https://dev.to/palks_studio/i-built-a-fully-automated-factur-x-en16931-batch-pipeline-in-php-no-saas-no-agent-just-cron-and-c84</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.amazonaws.com%2Fuploads%2Farticles%2Fzervxlynz5holook20rv.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.amazonaws.com%2Fuploads%2Farticles%2Fzervxlynz5holook20rv.png" alt="Automated Factur-X batch pipeline — full monthly cycle from CSV deposit to secured ZIP delivery, no SaaS, no database" width="799" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How a Monthly Cron Cycle Replaces an Entire Invoicing Platform
&lt;/h2&gt;

&lt;p&gt;The goal wasn't to automate individual tasks.&lt;/p&gt;

&lt;p&gt;It was to automate an entire monthly invoicing cycle.&lt;/p&gt;

&lt;p&gt;From CSV import to Factur-X generation, payment tracking, secure delivery, and revenue reporting, every step runs through the same deterministic pipeline.&lt;/p&gt;

&lt;p&gt;No SaaS.&lt;/p&gt;

&lt;p&gt;No database.&lt;/p&gt;

&lt;p&gt;No background service.&lt;/p&gt;

&lt;p&gt;Just PHP, cron, and business rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://palks-studio.com/en/batch-invoicing-facturx" rel="noopener noreferrer"&gt;View the system&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Palks-Studio/automation-system" rel="noopener noreferrer"&gt;View the GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why I built it this way
&lt;/h2&gt;

&lt;p&gt;The requirement was simple on paper: take client data every month, generate Factur-X EN16931 compliant invoices, track payments, and deliver everything securely.&lt;/p&gt;

&lt;p&gt;The constraint was harder: no SaaS dependency, no shared infrastructure, no implicit processing. Every step had to be explicit, traceable, and stoppable.&lt;/p&gt;

&lt;p&gt;That constraint shaped every architectural decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  The monthly cycle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Onboarding
&lt;/h3&gt;

&lt;p&gt;Before the first invoice is ever generated, every client is initialized automatically from a form submission. A unique identifier is generated, configuration files are created, archiving folders are structured, and payment tracking is prepared. No manual intervention required. The system is ready to process.&lt;/p&gt;

&lt;h3&gt;
  
  
  D1 to D7: CSV deposit and batch generation
&lt;/h3&gt;

&lt;p&gt;Each client deposits their billing data via a secured form. The system validates structure and formats strictly, with no implicit tolerance and no silent correction. A malformed field stops the entire client batch, isolates it, and triggers an automatic notification for correction before reprocessing.&lt;/p&gt;

&lt;p&gt;If valid, batch generation runs daily via cron. Invoice composition is driven entirely by business rules, including rates, VAT zones, service periods, and multi-line and multi-month billing. The output is a Factur-X EN16931-compliant PDF/A-3 document with embedded XML, making it both human-readable and machine-readable.&lt;/p&gt;

&lt;h3&gt;
  
  
  D8: B2B invoice and automatic pricing tier
&lt;/h3&gt;

&lt;p&gt;On day 8, the system detects the volume of invoices generated during the period and derives the applicable pricing tier automatically. The monthly B2B invoice is then generated and sent by email with the PDF attached. No manual input. The cron runs, the logic decides.&lt;/p&gt;

&lt;h3&gt;
  
  
  D13 to D14: The only manual step
&lt;/h3&gt;

&lt;p&gt;This is the single human touchpoint in the entire cycle: recording received bank transfers. Everything else is automated. This separation is intentional, because payment validation is a business decision, not a technical one.&lt;/p&gt;

&lt;h3&gt;
  
  
  D15: Balance recalculation and payment control
&lt;/h3&gt;

&lt;p&gt;Two operations run on day 15. First, client balances are recalculated from recorded payments. Unpaid clients are automatically deactivated, blocking any further send operations until the situation is resolved. Second, paid receipts are sent to clients with confirmed payment status via cron.&lt;/p&gt;

&lt;h3&gt;
  
  
  D16: Secured delivery
&lt;/h3&gt;

&lt;p&gt;The monthly ZIP is sent once. A deduplication guard ensures nothing can be sent twice, regardless of cron retries or manual triggers. Delivery uses a temporary token, making the file accessible only through a time-limited secured link. The archive is retained independently.&lt;/p&gt;




&lt;h2&gt;
  
  
  What makes it deterministic
&lt;/h2&gt;

&lt;p&gt;Every processing decision is explicit. There is no magic, no silent fallback, no implicit correction.&lt;/p&gt;

&lt;p&gt;If a CSV is invalid the client batch stops.&lt;br&gt;&lt;br&gt;
If a client is unpaid sends are blocked.&lt;br&gt;&lt;br&gt;
If a ZIP was already sent the guard refuses the duplicate.&lt;/p&gt;

&lt;p&gt;Every operation is timestamped in technical logs. Client state is controlled via explicit flags. The revenue journal is updated on every confirmed payment.&lt;/p&gt;

&lt;p&gt;The system is designed to be readable by anyone who picks it up. Not clever. Predictable.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;PHP 8: backend logic and cron execution
&lt;/li&gt;
&lt;li&gt;mPDF: PDF generation
&lt;/li&gt;
&lt;li&gt;factur-x (Python, Akretion): Factur-X XML injection and PDF/A-3 conversion
&lt;/li&gt;
&lt;li&gt;Flat-file storage: contracts, invoices, counters, logs, revenues
&lt;/li&gt;
&lt;li&gt;No database. No SaaS. No external API dependency&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Building this pipeline wasn't about automating invoice generation.&lt;/p&gt;

&lt;p&gt;It was about designing a system where every execution produces the same predictable result.&lt;/p&gt;

&lt;p&gt;Once the business rules were defined, cron simply became the engine that executes them.&lt;/p&gt;

&lt;p&gt;Automation wasn't the goal.&lt;/p&gt;

&lt;p&gt;Reliability was.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Further
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/content" rel="noopener noreferrer"&gt;Explore more technical articles&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://palks-studio.com/en/technical-notes" rel="noopener noreferrer"&gt;Read more technical notes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>productivity</category>
      <category>programming</category>
      <category>php</category>
    </item>
    <item>
      <title>How I Integrated Factur-X EN16931 into an Existing PHP Billing System</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Mon, 11 May 2026 10:17:35 +0000</pubDate>
      <link>https://dev.to/palks_studio/-how-i-integrated-factur-x-en16931-into-a-php-billing-system-without-a-database-or-saas-25ab</link>
      <guid>https://dev.to/palks_studio/-how-i-integrated-factur-x-en16931-into-a-php-billing-system-without-a-database-or-saas-25ab</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.amazonaws.com%2Fuploads%2Farticles%2F3bffajxx80sck12uae6f.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.amazonaws.com%2Fuploads%2Farticles%2F3bffajxx80sck12uae6f.png" alt="A Factur-X EN16931 compliant invoice — PDF/A-3 with embedded XML, generated on-premise without SaaS or ERP" width="800" height="1131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating Factur-X into an Existing PHP Application
&lt;/h2&gt;

&lt;p&gt;As the French e-invoicing reform (RFE) moved closer to implementation, I needed to make my billing system compliant without replacing the existing application. Rather than switching to a SaaS platform or adopting an ERP, I chose to integrate the Factur-X standard directly into an existing PHP application running on a standard Apache server.&lt;/p&gt;

&lt;p&gt;This article explains the architecture I built, what the standard actually requires, and the technical challenges I encountered along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Factur-X?
&lt;/h2&gt;

&lt;p&gt;Factur-X is a Franco-German standard for structured electronic invoicing that combines two elements into a single document:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A human-readable PDF/A-3 invoice
&lt;/li&gt;
&lt;li&gt;An embedded XML document compliant with the EN16931 European standard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are required. The PDF is intended for people, while the XML is intended for accounting software and automated processing. Together, they form a single electronic invoice.&lt;/p&gt;

&lt;p&gt;Although Factur-X originated in France and Germany, it aligns with the European EN16931 standard and is part of the broader transition toward mandatory e-invoicing across Europe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://palks-studio.com/en/facturx-invoice-generation" rel="noopener noreferrer"&gt;Try it&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://palks-studio.com/en/invoicing-without-saas" rel="noopener noreferrer"&gt;View System&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=gboIdyW5als&amp;amp;list=PLZEg9_eUFTGA" rel="noopener noreferrer"&gt;Watch the Complete Demo Playlist&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Palks-Studio/billing-system" rel="noopener noreferrer"&gt;View the GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The VAT Legal Mentions, the Part Most Developers Miss
&lt;/h2&gt;

&lt;p&gt;Generating a valid Factur-X file is only part of the job. The invoice itself must also include the correct VAT legal mention according to the client's location. Getting this wrong isn't a formatting mistake, it's a fiscal compliance issue.&lt;/p&gt;

&lt;p&gt;When the issuer is based in France, there are three possible situations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;French client&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The issuer is a micro-enterprise or is not subject to VAT. The invoice must include the following legal mention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TVA non applicable, art. 293B du CGI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No VAT is applied, no VAT column appears on the invoice, and the legal mention is mandatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EU client (B2B with a VAT number)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The transaction falls under the reverse charge mechanism, meaning the customer declares the VAT in their own country. The invoice must include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Autoliquidation – TVA due par le preneur, art. 283-2 du CGI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VAT is set to 0% and the legal mention replaces the usual VAT information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-EU client&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The service is exported outside the European Union. VAT exemption applies under the French tax code, and the invoice must include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exonération de TVA – art. 262 I du CGI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, VAT is set to 0%, but the legal mention remains mandatory.&lt;/p&gt;

&lt;p&gt;These aren't optional notes added for convenience. They explain why VAT is absent or transferred, and using the wrong one can become a compliance issue during a tax audit.&lt;/p&gt;

&lt;p&gt;In my billing system, the correct legal mention is selected automatically from the client's ISO country code and VAT number. The developer doesn't choose it manually. The business rules do.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Technical Chain: How Factur-X Is Actually Generated
&lt;/h2&gt;

&lt;p&gt;Generating a Factur-X invoice is split into two distinct phases, each handled by a dedicated component. This separation is intentional because XML generation and PDF/A-3 conversion solve different problems and have different technical requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Building the XML
&lt;/h3&gt;

&lt;p&gt;The first step is handled by a PHP component responsible for generating the EN16931-compliant XML document.&lt;/p&gt;

&lt;p&gt;It reads the invoice data, including issuer and client details, service lines, VAT amounts, totals, and payment terms, then maps every value to its corresponding EN16931 business term. The generated document follows the Factur-X Comfort profile, covering the information required for standard B2B invoicing.&lt;/p&gt;

&lt;p&gt;Several business rules must be respected during this phase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amounts use strict decimal formatting with no implicit rounding
&lt;/li&gt;
&lt;li&gt;VAT is aggregated by rate rather than by invoice line
&lt;/li&gt;
&lt;li&gt;Seller and buyer identifiers (SIREN, SIRET, VAT number) are placed in the appropriate XML elements according to the client's location
&lt;/li&gt;
&lt;li&gt;The VAT legal mention determined earlier is written as structured XML data, not only as visible text in the PDF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The generated XML is stored temporarily before the second phase begins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Injecting the XML into the PDF
&lt;/h3&gt;

&lt;p&gt;The second step is handled by a Python component using the Factur-X library developed by Akretion, one of the organizations behind the standard.&lt;/p&gt;

&lt;p&gt;Its role is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the PDF generated by the PHP billing engine using mPDF
&lt;/li&gt;
&lt;li&gt;Embed the EN16931 XML document
&lt;/li&gt;
&lt;li&gt;Convert the final document to the PDF/A-3b format required by Factur-X&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose Python for this stage because the Factur-X library provides reliable PDF/A-3 conversion and XML embedding without introducing heavy PHP dependencies. It also validates the generated XML against the EN16931 schemas before embedding it, providing an additional compliance check.&lt;/p&gt;

&lt;p&gt;The result is a single PDF/A-3 document containing the embedded XML, fully compliant with the Factur-X EN16931 Comfort profile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporary Files
&lt;/h3&gt;

&lt;p&gt;The XML only exists for as long as it is needed.&lt;/p&gt;

&lt;p&gt;It is written to a dedicated temporary directory, embedded into the PDF during the second phase, then deleted automatically once the final document has been generated. Only the completed Factur-X invoice is retained.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Architecture Delivers
&lt;/h2&gt;

&lt;p&gt;The result of this pipeline is a single document that is both human-readable and machine-readable: a Factur-X EN16931-compliant PDF/A-3 containing its embedded XML, ready for transmission to a French Partner Dematerialization Platform (PDP) or direct exchange with a client.&lt;/p&gt;

&lt;p&gt;Beyond compliance, this architecture provides several practical advantages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-Hosted by Design
&lt;/h3&gt;

&lt;p&gt;The entire generation pipeline runs on the same server as the billing system. No external API is called during invoice generation, and no financial data leaves the infrastructure.&lt;/p&gt;

&lt;p&gt;For organizations that require full control over their accounting data, this removes the dependency on third-party cloud services during document generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Independent from an ERP
&lt;/h3&gt;

&lt;p&gt;The system doesn't rely on an ERP or existing accounting software.&lt;/p&gt;

&lt;p&gt;Invoices are generated directly from the billing workflow, using the client information, service lines, VAT rules, and payment terms already managed by the application.&lt;/p&gt;

&lt;p&gt;This makes the solution suitable for businesses that need Factur-X compliance without replacing their existing processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compliance Built into the Workflow
&lt;/h3&gt;

&lt;p&gt;Compliance isn't added at the end of the process.&lt;/p&gt;

&lt;p&gt;VAT rates, legal mentions, and business rules are determined before the document is generated, then represented consistently in both the visible PDF and the embedded XML.&lt;/p&gt;

&lt;p&gt;The result isn't simply a document that looks compliant.&lt;/p&gt;

&lt;p&gt;It's a workflow designed to produce compliant invoices by construction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Integrating Factur-X into an existing billing system isn't only about generating XML or converting a PDF to PDF/A-3.&lt;/p&gt;

&lt;p&gt;The real challenge is translating business rules into structured data before the invoice is created.&lt;/p&gt;

&lt;p&gt;The architecture described here, PHP for XML generation and Python for PDF/A-3 conversion and XML embedding, has allowed me to integrate Factur-X into an existing application without introducing an ERP, a SaaS platform, or unnecessary complexity.&lt;/p&gt;

&lt;p&gt;If you're working on a similar integration, I hope this overview helps clarify where the real challenges lie.&lt;/p&gt;

&lt;p&gt;Once the business rules are modeled correctly, generating a compliant Factur-X invoice becomes the easiest part of the process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Further
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/content" rel="noopener noreferrer"&gt;Explore more technical articles&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://palks-studio.com/en/technical-notes" rel="noopener noreferrer"&gt;Read more technical notes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>backend</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Dependency as an Engineering Trade-Off</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Sun, 10 May 2026 14:49:48 +0000</pubDate>
      <link>https://dev.to/palks_studio/dependency-as-an-engineering-trade-off-322p</link>
      <guid>https://dev.to/palks_studio/dependency-as-an-engineering-trade-off-322p</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.amazonaws.com%2Fuploads%2Farticles%2F7t8zsgvdkykzro1n823g.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.amazonaws.com%2Fuploads%2Farticles%2F7t8zsgvdkykzro1n823g.png" alt="Tangled dependencies versus controlled backend architecture and long-term system stability" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stability, control and long-term maintainability in backend systems
&lt;/h2&gt;

&lt;p&gt;Dependencies are everywhere in modern software development.&lt;br&gt;&lt;br&gt;
Frameworks, libraries, external services, and specialized tools help accelerate development and avoid reinventing existing solutions.&lt;/p&gt;

&lt;p&gt;In many situations, this is a reasonable choice.&lt;br&gt;&lt;br&gt;
A dependency can reduce development time, provide proven functionality, and simplify complex implementations.&lt;/p&gt;

&lt;p&gt;However, every dependency also introduces a trade-off that is often invisible at first: part of the system’s behavior is no longer fully controlled by the people building it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Solving problems today, creating complexity tomorrow
&lt;/h2&gt;

&lt;p&gt;Adding a dependency usually solves an immediate problem.&lt;br&gt;&lt;br&gt;
A feature is implemented faster, with less effort, and the system moves forward quickly.&lt;/p&gt;

&lt;p&gt;Over time, however, dependencies evolve:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;breaking changes between versions
&lt;/li&gt;
&lt;li&gt;behavioral changes
&lt;/li&gt;
&lt;li&gt;reduced maintenance or abandonment
&lt;/li&gt;
&lt;li&gt;new technical constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system may continue to function, but its stability increasingly depends on external factors.&lt;/p&gt;

&lt;p&gt;Complexity rarely appears immediately.&lt;br&gt;&lt;br&gt;
It accumulates gradually as the system evolves.&lt;/p&gt;




&lt;h2&gt;
  
  
  When behavior escapes the system
&lt;/h2&gt;

&lt;p&gt;The more layers a system relies on, the harder it becomes to explain why it behaves in a certain way.&lt;/p&gt;

&lt;p&gt;An issue may originate from:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;application code
&lt;/li&gt;
&lt;li&gt;an intermediate library
&lt;/li&gt;
&lt;li&gt;a version change
&lt;/li&gt;
&lt;li&gt;or an external service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A backend system may suddenly behave differently after what appears to be a minor update:&lt;br&gt;&lt;br&gt;
a dependency changes an internal behavior, an external API modifies a response format, or a framework update introduces a new constraint.&lt;/p&gt;

&lt;p&gt;Nothing in the business logic was intentionally changed, yet the system itself no longer behaves the same way.&lt;/p&gt;

&lt;p&gt;This diffusion of responsibility increases uncertainty.&lt;br&gt;&lt;br&gt;
Understanding a problem often requires understanding multiple systems at once.&lt;/p&gt;

&lt;p&gt;At that point, the difficulty is no longer purely technical.&lt;br&gt;&lt;br&gt;
It becomes structural.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reducing dependencies means reducing uncertainty
&lt;/h2&gt;

&lt;p&gt;Reducing dependencies does not mean rejecting existing tools.&lt;br&gt;&lt;br&gt;
It means deciding what should truly belong to the system.&lt;/p&gt;

&lt;p&gt;Some functionalities are central enough to justify a simple, controlled implementation. Others can remain external without significant impact.&lt;/p&gt;

&lt;p&gt;The goal is not to minimize dependencies at all costs, but to limit those that directly influence core logic or critical system behavior.&lt;/p&gt;

&lt;p&gt;The fewer external elements a system depends on, the more predictable its behavior becomes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stability as a consequence of control
&lt;/h2&gt;

&lt;p&gt;The systems that are easiest to maintain are not necessarily those using the fewest tools.&lt;br&gt;&lt;br&gt;
They are often the ones where dependencies are clearly identified and limited to areas where they provide real value.&lt;/p&gt;

&lt;p&gt;When the essential parts of a system remain under control, external changes become less risky.&lt;/p&gt;

&lt;p&gt;Stability does not come from avoiding change, but from understanding what actually changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Balance rather than rejection
&lt;/h2&gt;

&lt;p&gt;Dependencies are an integral part of modern development.&lt;br&gt;&lt;br&gt;
They allow teams to build faster and benefit from shared knowledge.&lt;/p&gt;

&lt;p&gt;But every dependency shifts part of the control outside the system itself.&lt;/p&gt;

&lt;p&gt;Understanding this trade-off leads to better decisions: accepting dependencies when they genuinely simplify the problem, and reducing them when they introduce more uncertainty than value.&lt;/p&gt;

&lt;p&gt;Over time, these often invisible decisions determine whether a system remains understandable or gradually becomes difficult to evolve.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Further
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/content" rel="noopener noreferrer"&gt;Explore more technical articles&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://palks-studio.com/en/technical-notes" rel="noopener noreferrer"&gt;Read more technical notes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>security</category>
      <category>server</category>
    </item>
    <item>
      <title>AI Agents Writing All Your Code: Comfort or Loss of Control?</title>
      <dc:creator>Anthony Leignel</dc:creator>
      <pubDate>Fri, 24 Apr 2026 15:10:07 +0000</pubDate>
      <link>https://dev.to/palks_studio/ai-agents-writing-all-your-code-comfort-or-loss-of-control-5heb</link>
      <guid>https://dev.to/palks_studio/ai-agents-writing-all-your-code-comfort-or-loss-of-control-5heb</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.amazonaws.com%2Fuploads%2Farticles%2Faw6b8evycxyrsuq5ltqh.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.amazonaws.com%2Fuploads%2Farticles%2Faw6b8evycxyrsuq5ltqh.png" alt="AI agent controlling code and developer systems" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The new reflex: delegate everything
&lt;/h2&gt;

&lt;p&gt;Over the past few months, we’ve seen the rise of AI agents capable of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating full codebases
&lt;/li&gt;
&lt;li&gt;modifying existing projects
&lt;/li&gt;
&lt;li&gt;automating complex tasks
&lt;/li&gt;
&lt;li&gt;making technical decisions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reflex is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I’ll just let the AI handle it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it works.&lt;br&gt;&lt;br&gt;
At least, on the surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: we no longer know what we’re running
&lt;/h2&gt;

&lt;p&gt;When an agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;writes code
&lt;/li&gt;
&lt;li&gt;modifies files
&lt;/li&gt;
&lt;li&gt;restructures a project
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;who actually understands what’s going on?&lt;/p&gt;

&lt;p&gt;In many cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the code is accepted without review
&lt;/li&gt;
&lt;li&gt;the logic is not fully understood
&lt;/li&gt;
&lt;li&gt;entire parts become opaque
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We gain speed.&lt;br&gt;&lt;br&gt;
But we lose something fundamental:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;understanding.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A system that works… until it breaks
&lt;/h2&gt;

&lt;p&gt;It’s the same pattern we’ve seen before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it works
&lt;/li&gt;
&lt;li&gt;we stack layers
&lt;/li&gt;
&lt;li&gt;we trust it
&lt;/li&gt;
&lt;li&gt;then one day… it breaks
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And when it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no one knows where to look
&lt;/li&gt;
&lt;li&gt;no one understands the full logic
&lt;/li&gt;
&lt;li&gt;the system becomes hard to fix
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The question no one is asking
&lt;/h2&gt;

&lt;p&gt;Today, the focus is on performance, productivity, speed.&lt;/p&gt;

&lt;p&gt;But very few people ask the real question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;what happens when we no longer control what we execute?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because tomorrow, the issue might not be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a bug
&lt;/li&gt;
&lt;li&gt;a mistake
&lt;/li&gt;
&lt;li&gt;a bad implementation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But something deeper:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;total dependence on a system we don’t understand&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What if we start hearing about compromised agents?
&lt;/h2&gt;

&lt;p&gt;Today, it may sound exaggerated.&lt;/p&gt;

&lt;p&gt;But we’ve already seen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compromised dependencies
&lt;/li&gt;
&lt;li&gt;libraries injecting malicious code
&lt;/li&gt;
&lt;li&gt;popular tools becoming attack vectors
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the question is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;if an agent controls part of your code… what happens if it’s compromised?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And more importantly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;who is able to detect it?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The real issue isn’t AI
&lt;/h2&gt;

&lt;p&gt;AI isn’t the problem.&lt;/p&gt;

&lt;p&gt;It’s a powerful tool.&lt;br&gt;&lt;br&gt;
Useful.&lt;br&gt;&lt;br&gt;
Sometimes impressive.&lt;/p&gt;

&lt;p&gt;The problem is how we use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Assistant vs pilot
&lt;/h2&gt;

&lt;p&gt;There’s a huge difference between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using AI as an assistant
&lt;/li&gt;
&lt;li&gt;letting AI take control
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In one case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you gain speed
&lt;/li&gt;
&lt;li&gt;you keep control
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you accelerate
&lt;/li&gt;
&lt;li&gt;but you lose understanding
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Taking back control
&lt;/h2&gt;

&lt;p&gt;Using AI agents isn’t a bad thing.&lt;/p&gt;

&lt;p&gt;But a few simple principles make all the difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understand what is generated
&lt;/li&gt;
&lt;li&gt;limit automated layers
&lt;/li&gt;
&lt;li&gt;avoid delegating critical parts
&lt;/li&gt;
&lt;li&gt;keep logic simple and readable
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Technology is moving fast. Very fast.&lt;/p&gt;

&lt;p&gt;And AI agents will take more and more space.&lt;/p&gt;

&lt;p&gt;But as automation increases, control can decrease.&lt;/p&gt;

&lt;p&gt;And in technical systems, speed is not what makes them reliable.&lt;/p&gt;

&lt;p&gt;Understanding is.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go Further
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://palks-studio.com/en/content" rel="noopener noreferrer"&gt;Explore more technical articles&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://palks-studio.com/en/technical-notes" rel="noopener noreferrer"&gt;Read more technical notes&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://palks-studio.com" rel="noopener noreferrer"&gt;https://palks-studio.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ia</category>
      <category>programming</category>
      <category>security</category>
      <category>development</category>
    </item>
  </channel>
</rss>
