<?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: Aiepco</title>
    <description>The latest articles on DEV Community by Aiepco (@aiepco).</description>
    <link>https://dev.to/aiepco</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%2F4146656%2Ff6369a66-03fd-4a36-bd00-fcd305b6b0fe.png</url>
      <title>DEV Community: Aiepco</title>
      <link>https://dev.to/aiepco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aiepco"/>
    <language>en</language>
    <item>
      <title>Thuê ngoài lập trình PLC: 6 tài liệu bạn nên yêu cầu trước khi nghiệm thu</title>
      <dc:creator>Aiepco</dc:creator>
      <pubDate>Mon, 12 Oct 2026 01:56:15 +0000</pubDate>
      <link>https://dev.to/aiepco/thue-ngoai-lap-trinh-plc-6-tai-lieu-ban-nen-yeu-cau-truoc-khi-nghiem-thu-3dfe</link>
      <guid>https://dev.to/aiepco/thue-ngoai-lap-trinh-plc-6-tai-lieu-ban-nen-yeu-cau-truoc-khi-nghiem-thu-3dfe</guid>
      <description>&lt;p&gt;Khi một dây chuyền dừng vì sự cố, câu hỏi đầu tiên luôn là: &lt;em&gt;chúng ta có bản sao chương trình không, và có ai hiểu nó không?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rất nhiều nhà máy trả tiền cho một chương trình chạy được — nhưng không nhận được thứ giúp họ &lt;strong&gt;tự đứng được&lt;/strong&gt; sau đó. Dưới đây là 6 tài liệu nên có trong hợp đồng, kèm lý do vì sao từng cái quan trọng.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Chương trình gốc (SCL / LAD / FBD) — không chỉ bản trên máy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Là gì:&lt;/strong&gt; mã nguồn đầy đủ, có thể mở và biên dịch lại trong TIA Portal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vì sao quan trọng:&lt;/strong&gt; nhiều nơi chỉ nhận được "chương trình đang chạy trong PLC". Khi cần sửa một chi tiết, bạn không có gì để sửa ngoài việc đọc trực tiếp từ thiết bị — và mọi thay đổi đều rủi ro hơn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nếu thiếu:&lt;/strong&gt; lần bảo trì tiếp theo bạn phải thuê lại chính người cũ, với giá do họ quyết định.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Bảng phân bổ I/O (địa chỉ, tên tượng trưng, ý nghĩa công nghệ, loại tín hiệu)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Là gì:&lt;/strong&gt; bảng đối chiếu giữa địa chỉ vật lý (ví dụ &lt;code&gt;I0.3&lt;/code&gt;) và ý nghĩa thực tế ("cảm biến vị trí xy-lanh kẹp").&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vì sao quan trọng:&lt;/strong&gt; đây là cuốn từ điển giữa &lt;strong&gt;bản vẽ điện&lt;/strong&gt; và &lt;strong&gt;chương trình&lt;/strong&gt;. Không có nó, mỗi lần tìm một tín hiệu là một lần đoán.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nếu thiếu:&lt;/strong&gt; thời gian xử lý sự cố tăng gấp nhiều lần, và phụ thuộc hoàn toàn vào trí nhớ của người viết.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Mô tả cấu trúc POU (khối nào làm gì, gọi nhau ra sao)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Là gì:&lt;/strong&gt; sơ đồ phân cấp các khối chức năng — khối nào điều khiển xy-lanh, khối nào xử lý báo động, khối nào giao tiếp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vì sao quan trọng:&lt;/strong&gt; chương trình PLC không khó vì cú pháp, nó khó vì phải giữ trong đầu hàng chục quan hệ nhân quả. Một sơ đồ khối tốt biến việc đó thành đọc bản đồ.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nếu thiếu:&lt;/strong&gt; người mới phải dành vài ngày chỉ để hiểu chương trình được tổ chức thế nào.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Nhật ký biên dịch thực tế (compile log)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Là gì:&lt;/strong&gt; kết quả biên dịch thật từ TIA Portal, kèm danh sách cảnh báo và cách xử lý từng cảnh báo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vì sao quan trọng:&lt;/strong&gt; "không có lỗi" và "có biên dịch được hay không" là hai chuyện khác nhau. Nhật ký là &lt;strong&gt;bằng chứng cứng&lt;/strong&gt;, không phải lời hứa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nếu thiếu:&lt;/strong&gt; bạn không có cách nào xác nhận chương trình thực sự biên dịch sạch trước khi lên máy.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Báo cáo kiểm tra tĩnh
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Là gì:&lt;/strong&gt; kết quả kiểm tra tự động theo các quy tắc (nguy cơ chia cho 0, truy cập mảng ngoài biên, hằng số không tên, độ sâu lồng nhau, tỉ lệ chú thích…).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vì sao quan trọng:&lt;/strong&gt; nó phát hiện được &lt;strong&gt;một tầng&lt;/strong&gt; lỗi — và điều quan trọng không kém là báo cáo phải nói rõ &lt;strong&gt;tầng nào nó không kiểm tra được&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;⚠ Nhắc nhở trung thực: kiểm tra tĩnh &lt;strong&gt;không&lt;/strong&gt; thấy lỗi ngữ nghĩa. Ví dụ đổi &lt;code&gt;AND&lt;/code&gt; thành &lt;code&gt;OR&lt;/code&gt; trong điều kiện khởi động — sai hoàn toàn về logic — nhưng mọi công cụ dựa trên văn bản đều không phát hiện. Đừng để ai thuyết phục bạn rằng "đã kiểm tra tĩnh" nghĩa là "logic đúng".&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Hồ sơ kiểm chứng + danh sách điểm chưa quyết
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Là gì:&lt;/strong&gt; hai phần —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;hồ sơ kiểm chứng&lt;/strong&gt;: đối chiếu quỹ đạo mô phỏng, kiểm tra chuyển trạng thái và thời gian timer;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;danh sách điểm chưa quyết&lt;/strong&gt;: những điều kiện hiện trường &lt;strong&gt;chưa xác nhận&lt;/strong&gt;, mỗi điểm ghi rõ hai nhánh xử lý ("nếu đúng / nếu không đúng").&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Vì sao quan trọng:&lt;/strong&gt; phần thứ hai là thứ hầu như không ai giao. Nó cho bạn biết &lt;strong&gt;chương trình đang giả định điều gì về nhà máy của bạn&lt;/strong&gt; — và nếu giả định đó sai thì điều gì sẽ xảy ra.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nếu thiếu:&lt;/strong&gt; bạn nhận một chương trình "chạy được" mà không biết nó phụ thuộc vào những giả định nào.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cách ghi vào hợp đồng
&lt;/h2&gt;

&lt;p&gt;Ba câu có thể thêm vào mọi hợp đồng thuê ngoài lập trình PLC:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Danh mục bàn giao&lt;/strong&gt; liệt kê rõ 6 mục trên, kèm định dạng tệp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Điều kiện nghiệm thu&lt;/strong&gt; phải viết được phép kiểm chứng: "dây chuyền chạy đúng trình tự nêu ở mục 3.2" tốt hơn nhiều so với "logic chính xác".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cam kết nêu rõ phần không thuộc phạm vi&lt;/strong&gt;: thiết kế điện, tủ điện, ký duyệt an toàn chức năng (SIL/PL) — những việc này phải do bên có đủ năng lực thực hiện.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Điểm cuối cùng không phải để đẩy việc đi, mà để &lt;strong&gt;tránh hiểu sai ngay từ đầu&lt;/strong&gt; — nguyên nhân phổ biến nhất khiến công việc kéo dài.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chúng tôi làm việc thế nào
&lt;/h2&gt;

&lt;p&gt;Chúng tôi phát triển và kiểm chứng chương trình PLC cho Siemens S7-1200 / S7-1500 (SCL, LAD, FBD), và bàn giao cả 6 tài liệu trên như &lt;strong&gt;danh mục tiêu chuẩn&lt;/strong&gt; — không phải dịch vụ cộng thêm.&lt;/p&gt;

&lt;p&gt;Chúng tôi cũng nói rõ giới hạn của mình: chỉ làm chương trình và kiểm chứng; không thiết kế điện, không ký thay cho an toàn chức năng.&lt;/p&gt;

&lt;p&gt;Chi tiết về quy trình và bằng chứng: &lt;strong&gt;&lt;a href="https://plc.aiepco.com/vi/" rel="noopener noreferrer"&gt;plc.aiepco.com/vi/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bài viết dựa trên quy trình bàn giao thực tế của chúng tôi. Không có thông tin khách hàng nào trong bài.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>plc</category>
      <category>automation</category>
      <category>manufacturing</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Taking over someone else's PLC program: do these 4 things before you change a single line</title>
      <dc:creator>Aiepco</dc:creator>
      <pubDate>Sun, 11 Oct 2026 06:31:25 +0000</pubDate>
      <link>https://dev.to/aiepco/taking-over-someone-elses-plc-program-do-these-4-things-before-you-change-a-single-line-10d1</link>
      <guid>https://dev.to/aiepco/taking-over-someone-elses-plc-program-do-these-4-things-before-you-change-a-single-line-10d1</guid>
      <description>&lt;p&gt;You have just been handed a machine that runs — but nobody can explain it. The previous engineer left, the original documentation is gone, or the system was simply "commissioned in a hurry and never cleaned up".&lt;/p&gt;

&lt;p&gt;The most natural move is also the most expensive one: open TIA Portal and start editing.&lt;/p&gt;

&lt;p&gt;This is the sequence we use when a customer asks us to rescue or diagnose an inherited program. It is deliberately boring, and it exists because the exciting version fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you cannot start by fixing
&lt;/h2&gt;

&lt;p&gt;In a program that runs, the "obviously redundant" logic is very likely a patch someone added &lt;em&gt;after an incident&lt;/em&gt;. You can see the code; you cannot see the incident.&lt;/p&gt;

&lt;p&gt;If you change it without a baseline and without evidence, you do not remove the problem — you replace it with a different one. And the new problem is usually harder to find, because you introduced it yourself. Two weeks later you are back at the starting point, now with a second unknown in the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4 things, in order
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ① Capture evidence before you touch anything
&lt;/h3&gt;

&lt;p&gt;Archive everything you can currently get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the program uploaded from the PLC, as it is right now&lt;/li&gt;
&lt;li&gt;the diagnostic buffer&lt;/li&gt;
&lt;li&gt;the alarm history&lt;/li&gt;
&lt;li&gt;the actual state of the main I/O points&lt;/li&gt;
&lt;li&gt;the operator's description of &lt;em&gt;when&lt;/em&gt; things go wrong (spoken words are data too)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the raw material for every judgement you will make later. Once you start editing, the state of the machine at handover is gone forever. It is the one thing you cannot re-create.&lt;/p&gt;

&lt;h3&gt;
  
  
  ② Establish a version baseline
&lt;/h3&gt;

&lt;p&gt;Save the uploaded program with a dated archive entry and one sentence: &lt;strong&gt;"This version was uploaded from the machine on , unmodified."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It feels trivial. It is the only basis on which you will later be able to say &lt;em&gt;what&lt;/em&gt; you changed and &lt;em&gt;why&lt;/em&gt; — and the only starting point the next person can take over from.&lt;/p&gt;

&lt;h3&gt;
  
  
  ③ Shrink the search space with static analysis and reasoning
&lt;/h3&gt;

&lt;p&gt;Do not guess from the symptom. Run the program through tooling first: division-by-zero risk, array access without bounds checks, block call depth, unused variables, naming inconsistencies. You are not looking for the bug yet — you are trying to convert "one large unknown program" into "four suspicious blocks".&lt;/p&gt;

&lt;p&gt;Then list the plausible causes and rank them by likelihood. Resist the urge to work through them in the order they occurred to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  ④ Change only after the root cause is verified
&lt;/h3&gt;

&lt;p&gt;Confirm the root cause in simulation or by single-stepping on site. Then change. Then run a regression pass, because breaking B while fixing A is the normal case, not the exception.&lt;/p&gt;

&lt;p&gt;Every change gets the same archive treatment as step ②.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A ratio worth remembering:&lt;/strong&gt; when taking over an inherited program, about 90% of the time belongs to "understanding what it currently is", and 10% to "making it what I want". Invert that ratio and you will be back at the start in two weeks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What to ask the other side for
&lt;/h2&gt;

&lt;p&gt;Ask even if the answer is "we don't have it" — a "no" is itself information about how the system has been maintained.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A program backup. &lt;strong&gt;Any version, the older the better&lt;/strong&gt; — old versions are what you compare against.&lt;/li&gt;
&lt;li&gt;PLC model and firmware version (the order number is the accurate one).&lt;/li&gt;
&lt;li&gt;I/O list or electrical drawings — even a draft.&lt;/li&gt;
&lt;li&gt;Which faults have occurred historically, and what was done about them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who knows &lt;em&gt;why&lt;/em&gt; a particular piece of logic is written the way it is.&lt;/strong&gt; That person is worth more than any document.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scope note (what remote work can and cannot cover)
&lt;/h2&gt;

&lt;p&gt;Program-level investigation — uploading, static analysis, logic review, root-cause reasoning — can be done remotely, and that is how we usually start.&lt;/p&gt;

&lt;p&gt;What cannot be done remotely: wiring and hardware replacement, capturing real machine traces, and independent verification of safety functions. Those need hands on the machine.&lt;/p&gt;

&lt;p&gt;We publish this because we think the boundary should be stated up front rather than discovered in week three.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is a cross-post. The original article lives at &lt;a href="https://plc.aiepco.com/en/notes/takeover-checklist/" rel="noopener noreferrer"&gt;plc.aiepco.com/en/notes/takeover-checklist/&lt;/a&gt;, and our full engineering process is documented at &lt;a href="https://plc.aiepco.com/en/process/" rel="noopener noreferrer"&gt;plc.aiepco.com/en/process/&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>plc</category>
      <category>automation</category>
      <category>programming</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Outsourcing PLC programming: the 6 documents to ask for</title>
      <dc:creator>Aiepco</dc:creator>
      <pubDate>Tue, 29 Sep 2026 01:04:25 +0000</pubDate>
      <link>https://dev.to/aiepco/outsourcing-plc-programming-the-6-documents-to-ask-for-431i</link>
      <guid>https://dev.to/aiepco/outsourcing-plc-programming-the-6-documents-to-ask-for-431i</guid>
      <description>&lt;p&gt;Many plants pay for a program that runs — and never receive what they need to maintain it themselves.&lt;/p&gt;

&lt;p&gt;When a line stops, the first two questions are always: &lt;em&gt;do we have a copy of the program, and does anyone understand it?&lt;/em&gt; The list below exists to answer both. It is not moral advice about suppliers working harder — every item can be written into a contract as an acceptance criterion.&lt;/p&gt;

&lt;p&gt;We write PLC programs for a living (Siemens S7-1200/1500). This is what we hand over, and why each piece matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The program itself (SCL / LAD / FBD)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; complete source you can open and recompile in TIA Portal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; many sites only ever receive "the version currently running in the PLC." If all you hold is what is on the device, every future change becomes an online edit — with multiplied risk and cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without it:&lt;/strong&gt; the next maintenance job goes to whoever wrote it last, at whatever price they name.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. I/O allocation table
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; a cross-reference between physical addresses (&lt;code&gt;I0.3&lt;/code&gt;) and real meaning ("clamp cylinder position sensor") — address, symbolic name, process meaning, signal type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; this is the dictionary between the &lt;strong&gt;electrical drawings&lt;/strong&gt; and the &lt;strong&gt;program&lt;/strong&gt;. Without it, tracing a single signal is guesswork.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without it:&lt;/strong&gt; fault-finding takes many times longer and depends entirely on someone's memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. POU structure description
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; the hierarchy of function blocks — which block handles a cylinder, which handles alarms, which handles communications, and how they call each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; PLC programs are not hard because of syntax. They are hard because you must hold dozens of causal relationships in your head at once. A good structure diagram turns that into reading a map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without it:&lt;/strong&gt; a new engineer spends days just working out how the program is organised.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Compile log
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; the actual compilation output from TIA Portal, plus how each warning was handled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; "no errors" and "does it compile" are two different statements. The log is &lt;strong&gt;hard evidence&lt;/strong&gt;, not a promise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without it:&lt;/strong&gt; you have no way to confirm the program really compiles cleanly before it goes on the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Static analysis report
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; rule-based automated checks (division-by-zero risk, array bounds, unnamed constants, nesting depth, comment ratio), &lt;strong&gt;and an explicit statement of which layer it did not check&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; it catches &lt;strong&gt;one layer&lt;/strong&gt; of defects. Equally important is saying which layer it cannot see.&lt;/p&gt;

&lt;p&gt;⚠ An honest reminder: static analysis &lt;strong&gt;cannot&lt;/strong&gt; see semantic errors. Flip an &lt;code&gt;AND&lt;/code&gt; to &lt;code&gt;OR&lt;/code&gt; in a start condition — the logic is now exactly backwards — and no text- or structure-based tool will catch it. Never let "it passed static analysis" be used to imply "the logic is correct."&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Verification records + the open-items list
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; two parts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verification records:&lt;/strong&gt; simulation trace comparisons, state-transition and timer-timing checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-items list:&lt;/strong&gt; the site conditions that were &lt;strong&gt;never confirmed&lt;/strong&gt;, each stating the branch action for both "if yes" and "if no."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; that second part is what almost nobody hands over. It tells you &lt;strong&gt;which assumptions the program makes about your plant&lt;/strong&gt; — and what happens if an assumption is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without it:&lt;/strong&gt; you receive a program that "works" without knowing what it depends on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Put these three sentences in the contract
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deliverables list&lt;/strong&gt; — enumerate the six items above, with file formats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acceptance criteria&lt;/strong&gt; — write them as verifiable assertions. "Executes the sequence in section 3.2 correctly, 10 consecutive runs" beats "logic is correct."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope statement&lt;/strong&gt; — state what is &lt;em&gt;out&lt;/em&gt; of scope: electrical design, switchgear assembly, and functional safety (SIL/PL) sign-off must be done by a qualified party.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last one is not about pushing work away — it is about avoiding the misunderstanding that, in our experience, is the single most common reason a job drags on.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We develop and verify Siemens S7-1200/1500 PLC programs — and we hand over all six documents above as part of the standard scope, not as an add-on. How we work: &lt;a href="https://plc.aiepco.com" rel="noopener noreferrer"&gt;plc.aiepco.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>programming</category>
      <category>automation</category>
    </item>
    <item>
      <title>A linter that silently passes is worse than no linter at all</title>
      <dc:creator>Aiepco</dc:creator>
      <pubDate>Mon, 28 Sep 2026 08:43:36 +0000</pubDate>
      <link>https://dev.to/aiepco/a-linter-that-silently-passes-is-worse-than-no-linter-at-all-22o6</link>
      <guid>https://dev.to/aiepco/a-linter-that-silently-passes-is-worse-than-no-linter-at-all-22o6</guid>
      <description>&lt;p&gt;Our static analyzer had been reporting &lt;code&gt;no issues found&lt;/code&gt; on our PLC codebase for months.&lt;/p&gt;

&lt;p&gt;Then we built a fixture stuffed with deliberate bugs — and found that &lt;strong&gt;two of its rules had never fired on a single line of code.&lt;/strong&gt; Not once, since the day they were written.&lt;/p&gt;

&lt;p&gt;This is a post-mortem of our own verification toolchain failing, and the eight checks we now run before we trust any checker.&lt;/p&gt;




&lt;h2&gt;
  
  
  Context: what we verify, and why it matters
&lt;/h2&gt;

&lt;p&gt;We develop and verify Siemens S7-1200/1500 PLC programs (SCL, IEC 61131-3). The code runs production lines. A defect is not a bad user experience — it's a stopped line, scrapped product, or a safety incident.&lt;/p&gt;

&lt;p&gt;So we built a verification chain: static analysis rules, a self-test suite, and mutation testing. This article is about that toolchain failing at its job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding #1 — a rule that could never fire
&lt;/h2&gt;

&lt;p&gt;Here is what a normal run looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[motor_control_ref.scl]
  LOC=86 SLOC=61 comment_rate=27% McCabe≈9 nesting=3 magic_numbers=0
  POU: FUNCTION_BLOCK FB200_MotorControl (line 11)
  No static issues found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One green line. Looks like good news.&lt;/p&gt;

&lt;p&gt;Meanwhile we were writing a fixture — a file full of deliberately planted defects — to check whether the rules actually work. The result was unsettling: the fixture contained &lt;code&gt;#m_rB&lt;/code&gt; used as a divisor, and unguarded array access like &lt;code&gt;#m_Buffer[#i_Index]&lt;/code&gt;. The rules for &lt;em&gt;division-by-zero risk&lt;/em&gt; and &lt;em&gt;array-out-of-bounds risk&lt;/em&gt; reported nothing.&lt;/p&gt;

&lt;p&gt;The cause was in the regex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;/&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sr"&gt;*]&lt;/span&gt;&lt;span class="se"&gt;)\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;([&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z_&lt;/span&gt;&lt;span class="se"&gt;][&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z0-9_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks for a variable name following a slash. But &lt;strong&gt;SCL local variables carry a &lt;code&gt;#&lt;/code&gt; prefix&lt;/strong&gt; — the real code is &lt;code&gt;#m_rA / #m_rB&lt;/code&gt;. After the slash comes a space, then &lt;code&gt;#&lt;/code&gt;, then the name. The &lt;code&gt;#&lt;/code&gt; broke the match.&lt;/p&gt;

&lt;p&gt;Two rules, written months earlier, had never matched anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding #2 — the scary part is its siblings
&lt;/h2&gt;

&lt;p&gt;Fixing the regex took five minutes. The whole next day went into a different question: &lt;strong&gt;how many other rules are like this?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We ran every rule against the fixture. Three more problems surfaced:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Nature&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;VAR CONSTANT&lt;/code&gt; treated as a static variable&lt;/td&gt;
&lt;td&gt;All 6 constants flagged with "non-standard naming prefix"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;False positive&lt;/strong&gt; — rule too broad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safety-comment check too aggressive&lt;/td&gt;
&lt;td&gt;One function block produced &lt;strong&gt;17 warnings&lt;/strong&gt; of the same kind&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Noise flood&lt;/strong&gt; — real issues get buried&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call-graph parsed &lt;code&gt;ELSIF (...)&lt;/code&gt; as an FB call&lt;/td&gt;
&lt;td&gt;Call-depth analysis distorted&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Misparse&lt;/strong&gt; — parser boundary error&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three rules, three different diseases: &lt;strong&gt;too broad, too noisy, misparsed.&lt;/strong&gt; Together with the two that never fired, that's five failure modes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The common property: none of them raised an error.&lt;/strong&gt; The tool didn't crash, didn't throw, didn't warn. It just quietly produced a wrong conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding #3 — "passes" is not one thing
&lt;/h2&gt;

&lt;p&gt;We then ran mutation testing: inject a defect into clean code and see whether the chain catches it. Twelve mutation classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Syntax / structural&lt;/strong&gt; (missing &lt;code&gt;END_IF&lt;/code&gt;, missing semicolon, unbalanced parentheses, naming violations, magic numbers, stripped comments) → &lt;strong&gt;caught 6/6 by static analysis.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic&lt;/strong&gt; (&lt;code&gt;AND&lt;/code&gt; → &lt;code&gt;OR&lt;/code&gt;, deleted &lt;code&gt;NOT&lt;/code&gt;, inverted comparison, altered initial value, removed interlock) → &lt;strong&gt;caught 0/6.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most instructive number: we flipped the &lt;code&gt;AND&lt;/code&gt; in a start condition to &lt;code&gt;OR&lt;/code&gt; — the logic is now exactly backwards — and compared the text against the reference implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLEU-4 cumulative : 0.9981
Line-level match  : 97.6%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nearly a perfect score. &lt;strong&gt;Semantic mutations can make code look &lt;em&gt;more&lt;/em&gt; like the reference, not less.&lt;/strong&gt; No text- or structure-based tool can see them.&lt;/p&gt;

&lt;p&gt;So we put that limitation &lt;em&gt;into the report&lt;/em&gt; rather than tuning the numbers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Static analysis passing ≠ compiling ≠ being logically correct.&lt;br&gt;
Semantic correctness is the job of dynamic verification (simulation, on-machine trace comparison) and human review.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Finding #4 — the docs referenced scripts that didn't exist
&lt;/h2&gt;

&lt;p&gt;One more, and it is embarrassing rather than interesting: our framework documentation listed a set of tooling scripts. At that moment, &lt;strong&gt;none of them existed yet.&lt;/strong&gt; The documentation was complete; the implementation was zero.&lt;/p&gt;

&lt;p&gt;Same disease: interface declarations divorced from implementations. We added an assertion that scans the docs and fails if any referenced script or template is missing. Dead links don't get a second chance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed
&lt;/h2&gt;

&lt;p&gt;Three rules, all cheap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Every rule needs a fixture that must trigger it.&lt;/strong&gt; Write the rule, immediately build a sample containing that defect, assert it is reported. Freeze the sample as a regression test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every rule needs a counter-fixture that must &lt;em&gt;not&lt;/em&gt; trigger it.&lt;/strong&gt; Run it against compliant code, assert zero warnings. Without this you get the worse failure: the team learns to ignore all warnings, which is identical to having no checker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The toolchain must prove itself first.&lt;/strong&gt; Ours is 45 self-test assertions, a few seconds to run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ selftest_tools.py
assertions 45, failures 0
result: OK — toolchain behaves as declared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule is blunt: &lt;strong&gt;if the toolchain self-test isn't fully green, you may not use it to judge code.&lt;/strong&gt; An unverified tool's "pass" is not evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eight checks we now apply to any checker
&lt;/h2&gt;

&lt;p&gt;Abstracted, this is a checklist you can run against your own linters, analyzers or CI gates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Does every rule have a sample that must trigger it?&lt;/strong&gt; (No → you don't know whether it runs at all.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does every rule have a counter-sample that must not trigger it?&lt;/strong&gt; (No → it may be killing valid code.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the warning count sane?&lt;/strong&gt; 17 identical warnings in one file means the rule is noise, and the team will ignore it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When the tool fails, does it error or does it go silent?&lt;/strong&gt; Silent failure is the dangerous kind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there a meta-test guarding the tool itself?&lt;/strong&gt; Changing A and breaking B is the norm, not the exception.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have you written down what the tool deliberately does &lt;em&gt;not&lt;/em&gt; do?&lt;/strong&gt; Unstated boundaries are read as omniscience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does every file referenced in the documentation actually exist?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Before the word "passed", is there a line saying &lt;em&gt;which layer&lt;/em&gt; passed?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why publish this
&lt;/h2&gt;

&lt;p&gt;There's no growth chart or ROI curve here. It's a record of our own toolchain failing and being fixed.&lt;/p&gt;

&lt;p&gt;We publish it because of a fairly plain belief: &lt;strong&gt;if a supplier won't tell you whether their own checkers have been verified, you should trust what they hand you even less.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We prefer to say three separate sentences — static checks passed / it compiles / the logic is correct — rather than one vague "quality assured". Keeping them separate is more useful to the person paying for the work.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is a real internal post-mortem. All figures (14 static rules, 45 self-test assertions, 6/6 vs 0/6 mutation detection, BLEU 0.9981) come from actual tool output, and contain no client information.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We develop and verify Siemens S7-1200/1500 PLC programs — and we build the tooling that proves they're correct. How we work: &lt;a href="https://plc.aiepco.com" rel="noopener noreferrer"&gt;plc.aiepco.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>programming</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
