<?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: TalentOp Systems LLC</title>
    <description>The latest articles on DEV Community by TalentOp Systems LLC (@talentopsystems).</description>
    <link>https://dev.to/talentopsystems</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%2F4200342%2F91d01684-8ba7-435d-93d2-be69fa5c1951.png</url>
      <title>DEV Community: TalentOp Systems LLC</title>
      <link>https://dev.to/talentopsystems</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/talentopsystems"/>
    <language>en</language>
    <item>
      <title>Saw a notice at work about an H-1B filing? What it is, and what it is not</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Sat, 14 Nov 2026 05:45:03 +0000</pubDate>
      <link>https://dev.to/talentopsystems/saw-a-notice-at-work-about-an-h-1b-filing-what-it-is-and-what-it-is-not-41o</link>
      <guid>https://dev.to/talentopsystems/saw-a-notice-at-work-about-an-h-1b-filing-what-it-is-and-what-it-is-not-41o</guid>
      <description>&lt;p&gt;Checked against official sources on November 12, 2026&lt;/p&gt;

&lt;p&gt;You may see a new paper on a hallway board. Or a note on the company intranet. It talks about an H-1B filing, a wage, and a work location. It can look official, and a little sudden.&lt;/p&gt;

&lt;p&gt;It is supposed to be there. U.S. labor rules require that notice. Here is what those rules say, in plain words.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;An employer must give notice at the workplace when it files a Labor Condition Application (LCA) for an H-1B job. The notice may go out on the filing day, or in the 30 days just before. The usual way is a notice, on paper or online, that stays available for 10 days. The notice lists the job category, the wage offered, the dates, and the work locations. It also says how to complain to the Department of Labor (DOL).&lt;/p&gt;

&lt;p&gt;This notice is not a job ad. It is not a USCIS approval. And it is not the LCA itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the rule comes from
&lt;/h2&gt;

&lt;p&gt;The rule is in the U.S. labor rules, at 20 CFR 655.734. It comes from section 212(n) of the Immigration and Nationality Act.&lt;/p&gt;

&lt;p&gt;DOL's Wage and Hour Division describes the same duty in Fact Sheet #62M. U.S. Citizenship and Immigration Services (USCIS), which decides H-1B petitions, requires a certified LCA with the petition (Form I-129). The workplace notice must go out on the day of that DOL filing, or in the 30 days just before. That DOL filing happens before USCIS decides the case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some words, explained
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;H-1B:&lt;/strong&gt; a U.S. work visa for jobs that need a special skill and, most often, a college degree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LCA (Labor Condition Application):&lt;/strong&gt; the DOL form an employer submits before an H-1B petition. The employer uses it to state the pay on offer and the other terms of the job. The form number is ETA 9035 or 9035E.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notice of filing:&lt;/strong&gt; the short workplace notice that an LCA is being filed, or will be filed. This post is about that notice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ETA:&lt;/strong&gt; the Employment and Training Administration, the part of DOL that receives LCAs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Occupational classification:&lt;/strong&gt; the official job category on the LCA, such as software developer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conspicuous:&lt;/strong&gt; easy to see. The rule uses this word for places where workers can easily read the notice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collective bargaining representative:&lt;/strong&gt; a union that speaks for workers in that job category.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the notice must say
&lt;/h2&gt;

&lt;p&gt;When there is no union for that job, the notice must include all of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;that H-1B workers are being sought&lt;/li&gt;
&lt;li&gt;how many&lt;/li&gt;
&lt;li&gt;the job category&lt;/li&gt;
&lt;li&gt;the wages offered&lt;/li&gt;
&lt;li&gt;the period of employment&lt;/li&gt;
&lt;li&gt;the work location or locations&lt;/li&gt;
&lt;li&gt;that the LCA is available for public inspection at the H-1B employer's principal place of business in the U.S. (the office from which it runs its U.S. work) or at the worksite&lt;/li&gt;
&lt;li&gt;this exact statement: "Complaints alleging misrepresentation of material facts in the labor condition application and/or failure to comply with the terms of the labor condition application may be filed with any office of the Wage and Hour Division of the United States Department of Labor."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In plain words, that last line means a person may complain if they think the LCA states something untrue. They may also complain if the employer is not following it.&lt;/p&gt;

&lt;p&gt;The rule does not require the notice to name the worker.&lt;/p&gt;

&lt;h2&gt;
  
  
  When and where it goes up
&lt;/h2&gt;

&lt;p&gt;The employer must give this notice on the day it files the LCA, or in the 30 days just before.&lt;/p&gt;

&lt;p&gt;If a union represents workers in that job category, the employer gives the notice to the union.&lt;/p&gt;

&lt;p&gt;If there is no union, the employer uses one of two methods:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A paper notice.&lt;/strong&gt; It must go up in at least two easy-to-see places at each work site where an H-1B worker will work. It must be large enough and visible enough that workers in that job category can easily read it. It must stay up for a total of 10 days. The rule says suitable places include, but are not limited to, the same spots as wage-and-hour or job-safety posters. This duty applies even if the work site belongs to a client, not to the H-1B employer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An electronic notice.&lt;/strong&gt; This can be email, an intranet board, or a company web page that workers can actually open. It can also be another channel the employer already uses for job news. The rule also allows the employer to arrange for the notice to appear on a client's intranet, so the affected workers can see it. The notice must be easy for those workers to reach. It must stay available for 10 days. There is one exception for a direct, personal notice, such as an individual email. That message only has to be sent once in the allowed time window. If workers do not have practical computer access, a paper notice must go up, or each worker must receive a personal paper copy.&lt;/p&gt;

&lt;p&gt;The electronic notice goes to employees in that job category at each place of employment. That includes employees of another company that owns or runs the site.&lt;/p&gt;

&lt;h2&gt;
  
  
  A later work site in the same area
&lt;/h2&gt;

&lt;p&gt;Sometimes, after the LCA is filed, the employer sends an H-1B worker to a new work site. That site was not planned at the time of filing, but it still sits in the same work area listed on the LCA. The rules call this the "area of intended employment." In simple terms, it is the normal commuting area around the work address on the LCA.&lt;/p&gt;

&lt;p&gt;In that case, a paper or electronic notice must still go up at the new site, on or before the worker's first day there.&lt;/p&gt;

&lt;p&gt;A work site outside that area has other rules. Those rules are not the subject of this post. For a question about your own location, speak with a licensed immigration attorney.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the notice is not
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is not a job ad.&lt;/strong&gt; It does not invite you to apply for that role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is not a USCIS approval.&lt;/strong&gt; The notice is about the DOL filing. USCIS may not have decided the petition yet, and may not even have received it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is not the LCA itself.&lt;/strong&gt; The LCA is the certified form. By the H-1B worker's first day at the work site, the employer must give that worker a signed, certified copy of the LCA. If the worker asks, the employer must also provide Form ETA 9035CP, which is the form's set of cover pages. That duty sits in the same rule, but it is a different paper from the workplace notice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not have to stay up for the whole job.&lt;/strong&gt; For a paper notice, the required time is 10 days.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to do if you see one
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read it.&lt;/strong&gt; Note the job category, the wage, the dates, and the locations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Know that it is normal.&lt;/strong&gt; A required notice is not, by itself, a sign that something is wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you are the H-1B worker.&lt;/strong&gt; The posting notice is for the workplace. You should also receive your own copy of the certified LCA by your first day. If you do not have it, ask your employer in writing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If something on the notice looks wrong.&lt;/strong&gt; The notice itself says that complaints may be filed with any office of DOL's Wage and Hour Division. For a question about your own facts, speak with a licensed immigration attorney first.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A note before you go
&lt;/h2&gt;

&lt;p&gt;This post shares general information from official U.S. government pages. TalentOp Systems LLC is not giving legal advice here. Rules can change, and every case is different. For a question about your own situation, please speak with a licensed immigration attorney.&lt;/p&gt;

&lt;h2&gt;
  
  
  About TalentOp Systems LLC
&lt;/h2&gt;

&lt;p&gt;TalentOp Systems LLC is an IT staffing and IT software consulting company. It started in February 2025 and works with clients across the United States. It also offers project development and manpower and staffing solutions. It hires on W2 and C2C, and people on OPT or STEM OPT are welcome to apply. TalentOp Systems LLC is enrolled in E-Verify.&lt;/p&gt;

&lt;p&gt;TalentOp Systems is a direct vendor for Kroger, whose offices are in Blue Ash, Ohio.&lt;/p&gt;

&lt;p&gt;Need H-1B sponsorship? TalentOp Systems LLC provides it for hires who meet the role's requirements and eligibility.&lt;/p&gt;

&lt;p&gt;See open roles at &lt;a href="https://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt;, or write to &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>h1b</category>
      <category>immigration</category>
      <category>career</category>
      <category>jobs</category>
    </item>
    <item>
      <title>JavaScript says your date is one day early. Here is why, tested in four time zones</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Thu, 12 Nov 2026 14:12:33 +0000</pubDate>
      <link>https://dev.to/talentopsystems/javascript-says-your-date-is-one-day-early-here-is-why-tested-in-four-time-zones-54lh</link>
      <guid>https://dev.to/talentopsystems/javascript-says-your-date-is-one-day-early-here-is-why-tested-in-four-time-zones-54lh</guid>
      <description>&lt;p&gt;Checked against official sources on October 30, 2026&lt;/p&gt;

&lt;p&gt;A user picks November 2. Your app shows November 1. Nobody touched the date, and yet it moved.&lt;/p&gt;

&lt;p&gt;This one has been around for years. A Stack Overflow question titled &lt;a href="https://stackoverflow.com/questions/7556591/is-the-javascript-date-object-always-one-day-off" rel="noopener noreferrer"&gt;"Is the Javascript date object always one day off?"&lt;/a&gt; was asked in September 2011. When we opened it on October 30, 2026, it had over 380,000 views and a score above 470.&lt;/p&gt;

&lt;p&gt;We wrote a tiny repro and ran it with Node v24.22.1 under four time zones: Los Angeles, New York, UTC and Kolkata. All output below is copied from that run.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is going on, in plain words
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Date&lt;/code&gt; in JavaScript is not a calendar day. It is one exact moment in time, stored as milliseconds since 1970 in UTC. UTC is the world's base clock, with no time zone offset.&lt;/p&gt;

&lt;p&gt;So when you give it just a day, like &lt;code&gt;"2026-11-02"&lt;/code&gt;, JavaScript has to pick a moment. It picks midnight UTC.&lt;/p&gt;

&lt;p&gt;MDN's &lt;code&gt;Date&lt;/code&gt; reference says it directly: when there is no time zone offset, date-only strings are read as UTC, while date-time strings are read as local time. MDN calls this a "historical spec error". In other words, it was a mistake in the rules that can no longer be changed without breaking old sites.&lt;/p&gt;

&lt;p&gt;Midnight UTC is still the evening of the day before in the Americas. When you show that moment in local time, you get the earlier date.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug, run in four places
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;due&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2026-11-02"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;due&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&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="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleDateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"en-US"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TZ=America/Los_Angeles toISOString: 2026-11-02T00:00:00.000Z  getDate(): 1  shown: 11/1/2026
TZ=America/New_York    toISOString: 2026-11-02T00:00:00.000Z  getDate(): 1  shown: 11/1/2026
TZ=UTC                 toISOString: 2026-11-02T00:00:00.000Z  getDate(): 2  shown: 11/2/2026
TZ=Asia/Kolkata        toISOString: 2026-11-02T00:00:00.000Z  getDate(): 2  shown: 11/2/2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stored moment is the same everywhere. Only the shown day changes. In the two US zones, it is one day early.&lt;/p&gt;

&lt;p&gt;Notice that Kolkata looks fine here. That is why this bug slips past teams: whoever tests east of UTC sees nothing wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a time and the rule flips
&lt;/h2&gt;

&lt;p&gt;Now pass the same day with a time and no &lt;code&gt;Z&lt;/code&gt; on the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2026-11-02T00:00:00"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TZ=America/Los_Angeles getDate(): 2  toISOString: 2026-11-02T08:00:00.000Z
TZ=America/New_York    getDate(): 2  toISOString: 2026-11-02T05:00:00.000Z
TZ=UTC                 getDate(): 2  toISOString: 2026-11-02T00:00:00.000Z
TZ=Asia/Kolkata        getDate(): 2  toISOString: 2026-11-01T18:30:00.000Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time it is read as local midnight, so the day is right everywhere. Two strings that look almost the same follow two different rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same bug, going the other way
&lt;/h2&gt;

&lt;p&gt;Most people meet the bug when they show a date. Some meet it when they save one. A common habit is to turn a &lt;code&gt;Date&lt;/code&gt; into &lt;code&gt;"YYYY-MM-DD"&lt;/code&gt; with &lt;code&gt;toISOString().slice(0, 10)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;picked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// months start at 0, so 10 is November&lt;/span&gt;
&lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;picked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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="nf"&gt;slice&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="mi"&gt;10&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TZ=America/Los_Angeles saved as: 2026-11-02
TZ=America/New_York    saved as: 2026-11-02
TZ=UTC                 saved as: 2026-11-02
TZ=Asia/Kolkata        saved as: 2026-11-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the US zones are fine, and India is a day early. Local midnight in Kolkata is 18:30 the evening before in UTC, and &lt;code&gt;toISOString()&lt;/code&gt; always speaks UTC.&lt;/p&gt;

&lt;p&gt;So each half of the bug hides from a different team. Put both halves in one app and you can lose a day on the way in and on the way out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix we use: treat a day as a day
&lt;/h2&gt;

&lt;p&gt;If the value is a plain calendar day, like a birthday, a due date, or a start date, do not let it become a UTC moment. Split the string and build the date from its parts. The &lt;code&gt;new Date(year, monthIndex, day)&lt;/code&gt; form always uses local time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseDay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;            &lt;span class="c1"&gt;// "2026-11-02" -&amp;gt; local midnight&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&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="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatDay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;        &lt;span class="c1"&gt;// local Date -&amp;gt; "2026-11-02"&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"0"&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="sb"&gt;`${y}-${m}-${d}`&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TZ=America/Los_Angeles parseDay: 11/2/2026  round trip: 2026-11-02  UTC display: 11/2/2026
TZ=America/New_York    parseDay: 11/2/2026  round trip: 2026-11-02  UTC display: 11/2/2026
TZ=UTC                 parseDay: 11/2/2026  round trip: 2026-11-02  UTC display: 11/2/2026
TZ=Asia/Kolkata        parseDay: 11/2/2026  round trip: 2026-11-02  UTC display: 11/2/2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same day in all four zones, both ways.&lt;/p&gt;

&lt;p&gt;The "UTC display" column is the other common fix. Keep &lt;code&gt;new Date("2026-11-02")&lt;/code&gt;, but show it in UTC too, so both sides agree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2026-11-02"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleDateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"en-US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;timeZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"UTC"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works, but you have to remember &lt;code&gt;timeZone: "UTC"&lt;/code&gt; on every display call, and the &lt;code&gt;getUTCDate()&lt;/code&gt; family on every read. Miss one, and the bug is back. We find the parse-into-parts version harder to get wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus trap: "add one day" is not "add 24 hours"
&lt;/h2&gt;

&lt;p&gt;While a date is a moment, adding a day by adding milliseconds is risky. Most US clocks fall back one hour on Sunday, November 1, 2026. Look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&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="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;plus24h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;nextDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;nextDay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TZ=America/Los_Angeles +24h: Sun Nov 01 2026 23:00:00  setDate+1: Mon Nov 02 2026 00:00:00
TZ=America/New_York    +24h: Sun Nov 01 2026 23:00:00  setDate+1: Mon Nov 02 2026 00:00:00
TZ=UTC                 +24h: Mon Nov 02 2026 00:00:00  setDate+1: Mon Nov 02 2026 00:00:00
TZ=Asia/Kolkata        +24h: Mon Nov 02 2026 00:00:00  setDate+1: Mon Nov 02 2026 00:00:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Los Angeles and New York, that Sunday had 25 hours. Adding 24 hours left us at 11 PM on the same day. &lt;code&gt;setDate(getDate() + 1)&lt;/code&gt; moves by one calendar day, so it landed on Monday.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is going: Temporal
&lt;/h2&gt;

&lt;p&gt;MDN lists &lt;code&gt;Temporal&lt;/code&gt; as the newer date API. It has a type called &lt;code&gt;PlainDate&lt;/code&gt; that is just a day, with no time and no zone, which is exactly what a due date is. In Node v24.22.1 it is still behind a flag, so we ran it with &lt;code&gt;node --harmony-temporal&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PlainDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2026-11-02"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;days&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="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TZ=America/Los_Angeles PlainDate: 2026-11-02  day: 2  next: 2026-11-03
TZ=Asia/Kolkata        PlainDate: 2026-11-02  day: 2  next: 2026-11-03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check support in your own runtime and browsers before you ship it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick checks for your codebase
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Search for &lt;code&gt;new Date("&lt;/code&gt; with a date-only string. Each one is a suspect.&lt;/li&gt;
&lt;li&gt;Search for &lt;code&gt;toISOString().slice(0, 10)&lt;/code&gt; and &lt;code&gt;.split("T")[0]&lt;/code&gt;. Each one is a suspect too.&lt;/li&gt;
&lt;li&gt;Run your tests twice, with &lt;code&gt;TZ=America/Los_Angeles&lt;/code&gt; and &lt;code&gt;TZ=Asia/Kolkata&lt;/code&gt;. One zone west of UTC and one east of it catches both halves.&lt;/li&gt;
&lt;li&gt;Decide, per field, whether it is a calendar day or an exact moment. Store days as &lt;code&gt;"YYYY-MM-DD"&lt;/code&gt; text and moments as full timestamps with an offset.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Docs and threads we opened
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Stack Overflow: &lt;a href="https://stackoverflow.com/questions/7556591/is-the-javascript-date-object-always-one-day-off" rel="noopener noreferrer"&gt;Is the Javascript date object always one day off?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date" rel="noopener noreferrer"&gt;Date() constructor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format" rel="noopener noreferrer"&gt;Date, "Date time string format"&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About TalentOp Systems LLC
&lt;/h2&gt;

&lt;p&gt;Our team works out of Omaha, Nebraska, and we have been in business since February 2025. We do IT staffing, software consulting and project development, and we staff software roles for clients in banking, insurance, healthcare and retail, where one wrong day on a due date or a policy start date is a real problem. Roles can be W2 or C2C, and many run 12+ months.&lt;/p&gt;

&lt;p&gt;TalentOp Systems is a direct vendor for Kroger, whose offices are in Blue Ash, Ohio.&lt;br&gt;
E-Verify: yes, TalentOp Systems LLC is enrolled and E-Verified.&lt;br&gt;
Need H-1B sponsorship? TalentOp Systems LLC provides it for hires who meet the role's requirements and eligibility.&lt;br&gt;
We also take H-1B transfers, and OPT or STEM OPT candidates are welcome to reach out.&lt;/p&gt;

&lt;p&gt;Find us at &lt;a href="https://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt; or email &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Let go from an H-1B job early? Who pays for your trip home</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Fri, 06 Nov 2026 14:10:56 +0000</pubDate>
      <link>https://dev.to/talentopsystems/let-go-from-an-h-1b-job-early-who-pays-for-your-trip-home-i6m</link>
      <guid>https://dev.to/talentopsystems/let-go-from-an-h-1b-job-early-who-pays-for-your-trip-home-i6m</guid>
      <description>&lt;p&gt;Checked against official sources on November 4, 2026&lt;/p&gt;

&lt;p&gt;Losing a job is hard. On an H-1B, it can feel even harder. Your status, your lease, and maybe your family's plans all depend on that one job.&lt;/p&gt;

&lt;p&gt;Here is one rule that is easy to miss. If your employer lets you go early, the employer has to pay for your trip home. Here is what that rule says, in plain words.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;If an H-1B employer dismisses you before your approved stay ends, the employer must pay the "reasonable costs of return transportation." That means a fair, normal cost to travel back to the country you lived in before.&lt;/p&gt;

&lt;p&gt;If you quit on your own, the employer does not have to pay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the rule comes from
&lt;/h2&gt;

&lt;p&gt;The rule is in the U.S. immigration rules, at 8 CFR 214.2(h)(4)(iii)(E). It comes from section 214(c)(5) of the Immigration and Nationality Act.&lt;/p&gt;

&lt;p&gt;USCIS says the same thing on its H-1B page. Your employer is liable for the reasonable cost of your return trip if it ends your job before the end of your authorized stay. Your employer is not responsible for that cost if you resign by choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some words, explained
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;H-1B:&lt;/strong&gt; a U.S. work visa for jobs that need a special skill and, most often, a college degree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dismissed:&lt;/strong&gt; the employer ends the job. A layoff counts. So does being fired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorized stay:&lt;/strong&gt; the time the U.S. lets you stay in H-1B status. Your I-94 record and your approval notice show the dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abroad:&lt;/strong&gt; the rule defines this as your "last place of foreign residence." In simple terms, the place outside the U.S. where you lived before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasonable cost:&lt;/strong&gt; the rule does not give a dollar amount. It means a normal, fair travel cost, not a luxury one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When the rule applies
&lt;/h2&gt;

&lt;p&gt;All of these must be true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You hold H-1B status through this employer's petition.&lt;/li&gt;
&lt;li&gt;The employer ends your job. You did not choose to leave.&lt;/li&gt;
&lt;li&gt;It happens before your authorized stay ends.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rule covers any employer whose job offer was the reason you got or kept your H-1B status. So it can apply to a new employer after an H-1B transfer, not only your first one.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it does not apply
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You resign.&lt;/strong&gt; The rule says that if you end the job by choice, you were not dismissed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your stay has already ended.&lt;/strong&gt; The rule is about a dismissal before the end of the authorized stay.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the rule does not say
&lt;/h2&gt;

&lt;p&gt;Short rules leave gaps. Be careful not to read more into this one than it says.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It names the worker. It does not mention a spouse or children.&lt;/li&gt;
&lt;li&gt;It does not say the employer must give you cash instead of a ticket.&lt;/li&gt;
&lt;li&gt;It does not say you must leave the U.S. right away. Many people look at other options first, such as a new job. Those options have their own rules and time limits. Ask an attorney about your own dates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the employer cares too
&lt;/h2&gt;

&lt;p&gt;This rule is tied to another one, from the Department of Labor (DOL).&lt;/p&gt;

&lt;p&gt;An H-1B employer must keep paying the worker's wage, even when there is no work to do. That duty ends only after a "bona fide termination," which means a real end to the job.&lt;/p&gt;

&lt;p&gt;DOL says the best proof of a real end has three parts. The employer tells USCIS the job has ended. The employer asks for the petition to be cancelled. And the employer pays for the worker's trip home where USCIS rules require it.&lt;/p&gt;

&lt;p&gt;So paying for the trip home is not just a kindness. It is part of how an employer shows the job truly ended.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do if you are let go
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ask for the end date in writing.&lt;/strong&gt; Keep any letter or email that says your job ended.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask about the return trip.&lt;/strong&gt; Ask your employer, in writing, how it will cover the reasonable cost of your trip home.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep your papers.&lt;/strong&gt; Save your approval notice, your I-94 record, pay stubs, and all messages about your last day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If the employer refuses.&lt;/strong&gt; The rule says the worker should tell the USCIS Service Center that decided the petition, in writing. USCIS keeps that complaint in the petition file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talk to an attorney early.&lt;/strong&gt; Your own facts matter, and the clock may already be running on other choices.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A note before you go
&lt;/h2&gt;

&lt;p&gt;This post shares general information from official U.S. government pages. TalentOp Systems LLC is not giving legal advice here. Rules can change, and every case is different. For a question about your own situation, please speak with a licensed immigration attorney.&lt;/p&gt;

&lt;h2&gt;
  
  
  About TalentOp Systems LLC
&lt;/h2&gt;

&lt;p&gt;TalentOp Systems LLC is an IT staffing and IT software consulting company. It started in February 2025 and works with clients across the United States. It also offers project development and manpower and staffing solutions. It hires on W2 and C2C, and people on OPT or STEM OPT are welcome to apply. TalentOp Systems LLC is enrolled in E-Verify.&lt;/p&gt;

&lt;p&gt;TalentOp Systems is a direct vendor for Kroger, whose offices are in Blue Ash, Ohio.&lt;/p&gt;

&lt;p&gt;Need H-1B sponsorship? TalentOp Systems LLC provides it for hires who meet the role's requirements and eligibility.&lt;/p&gt;

&lt;p&gt;See open roles at &lt;a href="https://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt;, or write to &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>h1b</category>
      <category>immigration</category>
      <category>career</category>
      <category>jobs</category>
    </item>
    <item>
      <title>pandas chunks fixed my memory error, then quietly broke my averages</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Mon, 02 Nov 2026 14:14:09 +0000</pubDate>
      <link>https://dev.to/talentopsystems/pandas-chunks-fixed-my-memory-error-then-quietly-broke-my-averages-2p3a</link>
      <guid>https://dev.to/talentopsystems/pandas-chunks-fixed-my-memory-error-then-quietly-broke-my-averages-2p3a</guid>
      <description>&lt;p&gt;Checked against official sources on October 30, 2026&lt;/p&gt;

&lt;p&gt;Here is a bug that does not crash. It just gives you a wrong number and lets you ship it.&lt;/p&gt;

&lt;p&gt;You have a CSV that is too big for pandas. You switch to &lt;code&gt;chunksize&lt;/code&gt;. The memory error goes away, the script finishes, and you move on. But if one ID's rows sit on both sides of a chunk edge, some per-ID results are now wrong. Nothing tells you.&lt;/p&gt;

&lt;p&gt;We built a test file, broke it on purpose, and fixed it two ways. Every output block below was pasted from our own runs with pandas 3.0.6 and Python 3.13.5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this came from
&lt;/h2&gt;

&lt;p&gt;A Python learner &lt;a href="https://www.reddit.com/r/learnpython/comments/16wb4xa/how_to_determine_the_chunk_limit_based_on_the/" rel="noopener noreferrer"&gt;asked about it on Reddit&lt;/a&gt;. Their file was too large for a 16 GB machine. They needed stats for each identifier, such as a row count per ID. They saw the trap right away: with chunks, one ID can be split across two reads. Even loading only the ID column was too much memory for them.&lt;/p&gt;

&lt;p&gt;So there are two jobs here. Make it fit in memory. Then make sure the answer is still right. The first job is easy. The second one is where people get hurt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test file
&lt;/h2&gt;

&lt;p&gt;We made fake patient vital signs, sorted by patient ID. No real patient data was used.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4,000,000 rows in total (the file is 172 MB)&lt;/li&gt;
&lt;li&gt;50,000 patients&lt;/li&gt;
&lt;li&gt;Five columns: &lt;code&gt;patient_id&lt;/code&gt;, &lt;code&gt;ts&lt;/code&gt;, &lt;code&gt;heart_rate&lt;/code&gt;, &lt;code&gt;spo2&lt;/code&gt; and &lt;code&gt;note&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each method ran in a fresh Python process, so the "peak RSS" number is clean. Peak RSS is the most memory the process used at any moment. Python's &lt;code&gt;resource.getrusage&lt;/code&gt; gave us that number.&lt;/p&gt;

&lt;p&gt;Chunks were 500,000 rows each. That gives 8 chunks and 7 edges, and all 7 edges landed in the middle of a patient.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, read less
&lt;/h2&gt;

&lt;p&gt;Before you reach for chunks, shrink what you load. The pandas docs page &lt;a href="https://pandas.pydata.org/docs/user_guide/scale.html" rel="noopener noreferrer"&gt;"Scaling to large datasets"&lt;/a&gt; makes two points that matter here. Load only the columns you need. Use smaller data types.&lt;/p&gt;

&lt;p&gt;One handy type is &lt;code&gt;category&lt;/code&gt;. It stores each distinct text value once and keeps a small number per row. For a column like patient ID, that saves a lot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;full_load&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mean_hr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;full_load_slim&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usecols&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                     &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int16&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;observed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mean_hr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;full_load       peak RSS     614 MB   time   4.1s   patients 50000
full_load_slim  peak RSS     178 MB   time   0.9s   patients 50000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both gave the same answer. The slim load used under a third of the memory. If your file is only a bit too big, stop here. You may not need chunks at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The chunk code most of us write first
&lt;/h2&gt;

&lt;p&gt;Group each chunk. Stack the small results. Group again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunks_wrong&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usecols&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                             &lt;span class="n"&gt;chunksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mean_hr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()}))&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                    &lt;span class="n"&gt;mean_hr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mean_hr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mean&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is fast and small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chunks_wrong    peak RSS     123 MB   time   1.1s   patients 50000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we checked it against the full load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chunks_wrong    rows match: True   patients with wrong mean: 7   worst error: 2.827
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Row counts were fine. A count from one chunk plus a count from the next is the true count.&lt;/p&gt;

&lt;p&gt;The means were not fine. Seven patients were wrong, which is one per chunk edge. The code took an average of two averages. That gives a small piece of data the same weight as a big piece.&lt;/p&gt;

&lt;p&gt;A quick example. One patient has 40 readings with an average of 80 in chunk one. The same patient has 2 readings with an average of 120 in chunk two. The average of 80 and 120 is 100. The true average of all 42 readings is about 82.&lt;/p&gt;

&lt;p&gt;Seven bad rows out of 50,000 is the scary part. Random spot checks will almost never land on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix one: carry sums and counts
&lt;/h2&gt;

&lt;p&gt;Only carry numbers that you can safely add. For an average, that means the sum and the count. Add them up across all chunks. Divide once at the very end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunks_right&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usecols&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                             &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int16&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;chunksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int64&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                         &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mean_hr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;size&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;total.add(part, fill_value=0)&lt;/code&gt; matches rows by patient ID. If a patient is missing from one side, that side counts as 0. The same pandas docs page uses this &lt;code&gt;add&lt;/code&gt; with &lt;code&gt;fill_value=0&lt;/code&gt; pattern to count values across many files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chunks_right    peak RSS     107 MB   time   1.1s   patients 50000
chunks_right    rows match: True   patients with wrong mean: 0   worst error: 0.000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every patient was right, and this was the lowest memory we measured. The running total holds one row per patient, so it does not grow with the length of the file.&lt;/p&gt;

&lt;p&gt;The same trick works for more stats. Min and max combine safely. For variance, carry three things: the count, the sum, and the sum of squares.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix two: medians need the rows
&lt;/h2&gt;

&lt;p&gt;Some results cannot be built from parts you add up. The median is one. "The last reading per patient" is another. For these, you need all of an ID's rows in one place.&lt;/p&gt;

&lt;p&gt;Our file is sorted by ID, and that makes it doable. At the end of each chunk, only the last ID might spill into the next chunk. So hold back that ID's rows and stick them on the front of the next chunk. Every other ID in the chunk is complete, so finish it now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;median_carry&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;carry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;COLS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;carry&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ignore_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;last_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;is_last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;last_id&lt;/span&gt;
        &lt;span class="n"&gt;carry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;is_last&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;          &lt;span class="c1"&gt;# may continue in the next chunk
&lt;/span&gt;        &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;is_last&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patient_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heart_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;median_full   peak RSS   233 MB   time  1.5s   patients 50000   duplicate ids 0
median_carry  peak RSS   123 MB   time  1.4s   patients 50000   duplicate ids 0
medians identical: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same medians as the full load, about half the memory, and no patient counted twice.&lt;/p&gt;

&lt;p&gt;Two warnings before you copy this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It only works if the file is truly sorted by ID. Check that first. If it is not, sort it once with a tool made for big files.&lt;/li&gt;
&lt;li&gt;If one ID has more rows than a whole chunk, the carried rows keep growing until that ID ends. Only a giant ID makes that a problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When pandas is the wrong tool
&lt;/h2&gt;

&lt;p&gt;The pandas docs say chunking works best when the chunks need little or no coordination. They also say &lt;code&gt;groupby&lt;/code&gt; is much harder to do in chunks. If your job needs joins, lots of group stats, or distinct counts, we find two other routes are often simpler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load it into a database once.&lt;/strong&gt; SQLite ships with Python. Write each chunk into a table, add an index on the ID, then use plain SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a library that streams for you.&lt;/strong&gt; Some DataFrame libraries can scan a CSV lazily and handle the chunk work inside.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Before you trust a chunked number
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Load only the columns you need. Try &lt;code&gt;category&lt;/code&gt; and small int types.&lt;/li&gt;
&lt;li&gt;Measure peak memory, one method per process.&lt;/li&gt;
&lt;li&gt;Use chunks only if you still have to.&lt;/li&gt;
&lt;li&gt;Carry values that add up safely: counts, sums, min and max.&lt;/li&gt;
&lt;li&gt;Never average averages. Keep the sum and the count, and divide at the end.&lt;/li&gt;
&lt;li&gt;For medians or "last value", sort by ID and carry the edge rows forward.&lt;/li&gt;
&lt;li&gt;Run the chunked version and a full load on a small sample, and compare them.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Who wrote this
&lt;/h2&gt;

&lt;p&gt;TalentOp Systems LLC is an IT staffing and IT software consulting company in Omaha, Nebraska, and we have been working since February 2025. We place data and software people on projects for clients in banking, insurance, healthcare and retail, where a number that is quietly wrong can cost real money. We hire across the US on W2 and C2C, often for 12+ month contracts.&lt;/p&gt;

&lt;p&gt;TalentOp Systems is a direct vendor for Kroger, whose offices are in Blue Ash, Ohio.&lt;br&gt;
We are an E-Verified employer.&lt;br&gt;
Need H-1B sponsorship? TalentOp Systems LLC provides it for hires who meet the role's requirements and eligibility.&lt;br&gt;
People on OPT or STEM OPT can apply too.&lt;/p&gt;

&lt;p&gt;See our open roles at &lt;a href="https://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt;, or write to &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>pandas</category>
      <category>datascience</category>
      <category>performance</category>
    </item>
    <item>
      <title>Two requests, one rule, two rows: fixing the INSERT ... WHERE NOT EXISTS race in PostgreSQL</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Thu, 29 Oct 2026 02:04:42 +0000</pubDate>
      <link>https://dev.to/talentopsystems/two-requests-one-rule-two-rows-fixing-the-insert-where-not-exists-race-in-postgresql-dfj</link>
      <guid>https://dev.to/talentopsystems/two-requests-one-rule-two-rows-fixing-the-insert-where-not-exists-race-in-postgresql-dfj</guid>
      <description>&lt;p&gt;A rule like "one comment per user per hour" looks easy. You check for a recent row, and you only insert if there is none. It works in every test you write by hand. Then two requests arrive at the same moment, and the rule quietly breaks.&lt;/p&gt;

&lt;p&gt;We set up the exact case on PostgreSQL 17.5, made it fail on purpose, and tried four fixes. Every number below comes from those runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question
&lt;/h2&gt;

&lt;p&gt;Someone on r/PostgreSQL asked about this query. The goal was to let a user post only one comment per hour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'my comment'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'1 hour'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They asked a fair question. If two threads run this at the same time, can two comments slip through? People in the thread said yes. We wanted to show why, and what to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it breaks
&lt;/h2&gt;

&lt;p&gt;PostgreSQL's default mode is called Read Committed. The docs say each query in this mode sees a snapshot of the database taken "the instant the query begins to run." It never sees rows that other transactions have not committed yet.&lt;/p&gt;

&lt;p&gt;So here is the timeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Request A runs the query. It finds no recent comment, so it inserts one. A has not committed yet.&lt;/li&gt;
&lt;li&gt;Request B runs the same query. A's new row is not committed, so B cannot see it. B also finds no recent comment, and inserts one.&lt;/li&gt;
&lt;li&gt;Both commit. The user now has two comments in the same hour.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nothing here is a bug in PostgreSQL. The check and the insert are in one statement, but the check only looks at committed data. There is no unique key for the database to defend, so nothing makes B wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test setup
&lt;/h2&gt;

&lt;p&gt;Our test opens two connections. Request A runs its insert and stays open. Request B runs while A is still open. After half a second, A commits. Then we count rows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&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 core of the Python test, using psycopg2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DSN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DSN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from request A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# A is inside its transaction
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;second&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from request B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# B runs while A is open
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;waited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plain query fails just as the thread said:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;naive NOT EXISTS       rows=2  B inserted=1  B waited=0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two rows, and B never waited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 1: an advisory lock per user
&lt;/h2&gt;

&lt;p&gt;An advisory lock is a lock with a meaning you choose. PostgreSQL does not attach it to any table. &lt;code&gt;pg_advisory_xact_lock&lt;/code&gt; holds the lock until the transaction ends, so you never have to free it by hand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;advisory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Lock first, in its own statement.
&lt;/span&gt;    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT pg_advisory_xact_lock(42, %s)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NAIVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;42&lt;/code&gt; is just a number we picked to mean "comment limit." The second number is the user id. So two users never block each other.&lt;/p&gt;

&lt;p&gt;The detail that matters: &lt;strong&gt;the lock must be its own statement, before the check.&lt;/strong&gt; In Read Committed, each statement gets a fresh snapshot. B waits on the lock. When A commits, B gets the lock, and its next statement takes a new snapshot that includes A's row.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;advisory lock          rows=1  B inserted=0  B waited=0.5s
advisory, 2 users      rows=2  B inserted=1  B waited=0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;B waited for A, then saw the row and inserted nothing. A second user was not slowed down at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2: lock the user's row
&lt;/h2&gt;

&lt;p&gt;If you already have a &lt;code&gt;users&lt;/code&gt; table, you can lock the user's row instead. &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; makes any other transaction that tries to lock the same row wait.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;row_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT 1 FROM users WHERE id = %s FOR UPDATE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
    &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NAIVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FOR UPDATE on users    rows=1  B inserted=0  B waited=0.5s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same result. This one is easy to read in code review, because the lock is on a real thing. The cost: anything else that locks that user row will wait too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 3: let the database enforce the rule
&lt;/h2&gt;

&lt;p&gt;Locks only work if every code path takes them. A new script or a second service might skip the lock. A constraint cannot be skipped.&lt;/p&gt;

&lt;p&gt;PostgreSQL has exclusion constraints. They say "no two rows may match on these tests." With the &lt;code&gt;btree_gist&lt;/code&gt; extension, we can say "same user AND overlapping one-hour windows" is not allowed.&lt;/p&gt;

&lt;p&gt;Our first try failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="n"&gt;EXCLUDE&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gist&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tstzrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'1 hour'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- ERROR: functions in index expression must be marked IMMUTABLE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding an interval to a &lt;code&gt;timestamptz&lt;/code&gt; can depend on the time zone setting, so PostgreSQL will not use it in an index. The fix is to store the window as its own column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;btree_gist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;active_window&lt;/span&gt; &lt;span class="n"&gt;tstzrange&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;one_comment_per_hour&lt;/span&gt;
  &lt;span class="n"&gt;EXCLUDE&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gist&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active_window&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then insert with &lt;code&gt;ON CONFLICT DO NOTHING&lt;/code&gt;, so a blocked comment is skipped and does not throw an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;active_window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tstzrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'1 hour'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;NOTHING&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exclusion constraint   rows=1  B inserted=0  B waited=0.5s
exclusion, 2 users     rows=2  B inserted=1  B waited=0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to check if you copy an answer from the thread. One reply showed an exclusion constraint with only the time range, and no &lt;code&gt;user_id WITH =&lt;/code&gt;. That version stops &lt;em&gt;any two users&lt;/em&gt; from posting close together. Always put the user column in.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;tstzrange&lt;/code&gt; built this way includes its start and leaves out its end. So a comment exactly one hour later does not overlap and is allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 4: SERIALIZABLE, plus a retry
&lt;/h2&gt;

&lt;p&gt;You can also leave the query alone and run both transactions at the SERIALIZABLE level. PostgreSQL watches for reads and writes that could not happen in any one-at-a-time order, and it cancels one of the transactions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A committed
B commit failed, SQLSTATE 40001 - could not serialize access due to read/write dependencies among transactions
serializable naive: rows = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is correct, but your code must catch &lt;code&gt;40001&lt;/code&gt; and run the whole transaction again. The PostgreSQL docs say the same thing: apps at this level must be ready to retry.&lt;/p&gt;

&lt;h2&gt;
  
  
  A trap we found: advisory lock plus REPEATABLE READ
&lt;/h2&gt;

&lt;p&gt;We also ran Fix 1 with both transactions at REPEATABLE READ. It let two rows through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;B committed
repeatable read + advisory: rows = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The docs explain why. At REPEATABLE READ, the snapshot is taken at the start of the first statement in the transaction. Here that first statement is the lock call itself. So B's snapshot is fixed &lt;em&gt;before&lt;/em&gt; it waits for the lock, and it never sees A's row. Fixes 1 and 2 depend on Read Committed. If your app or framework changes the isolation level, use Fix 3 or Fix 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one should you pick?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fix 3, the exclusion constraint,&lt;/strong&gt; if the rule must hold no matter which code writes to the table. It is our first choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix 1 or 2&lt;/strong&gt; if the rule is complex, such as "five comments per hour," which a simple constraint cannot express. Keep the default Read Committed level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix 4&lt;/strong&gt; if your app already runs at SERIALIZABLE and has a retry loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In every case, keep the lock or the transaction short. B waits for as long as A holds on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docs we read
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL 17, Transaction Isolation: &lt;a href="https://www.postgresql.org/docs/17/transaction-iso.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/17/transaction-iso.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL 17, Explicit Locking, Advisory Locks: &lt;a href="https://www.postgresql.org/docs/17/explicit-locking.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/17/explicit-locking.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL 17, Advisory Lock Functions: &lt;a href="https://www.postgresql.org/docs/17/functions-admin.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/17/functions-admin.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL 17, Constraints, Exclusion Constraints: &lt;a href="https://www.postgresql.org/docs/17/ddl-constraints.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/17/ddl-constraints.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL 17, INSERT and ON CONFLICT: &lt;a href="https://www.postgresql.org/docs/17/sql-insert.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/17/sql-insert.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL 17, btree_gist: &lt;a href="https://www.postgresql.org/docs/17/btree-gist.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/17/btree-gist.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL 17, Range Types: &lt;a href="https://www.postgresql.org/docs/17/rangetypes.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/17/rangetypes.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  From TalentOp Systems LLC
&lt;/h2&gt;

&lt;p&gt;We are TalentOp Systems LLC, based in Omaha, Nebraska. We started in February 2025. We work in IT staffing, software consulting, project development, and manpower and staffing for clients in banking, insurance, healthcare and retail.&lt;/p&gt;

&lt;p&gt;TalentOp Systems is a direct vendor for Kroger, whose offices are in Blue Ash, Ohio.&lt;br&gt;
TalentOp Systems LLC is E-Verified.&lt;br&gt;
Need H-1B sponsorship? TalentOp Systems LLC provides it for hires who meet the role's requirements and eligibility.&lt;br&gt;
OPT and STEM OPT graduates are welcome to apply.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="http://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt;. Email: &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>sql</category>
      <category>database</category>
      <category>backend</category>
    </item>
    <item>
      <title>An old tab meets a new deploy: fixing React.lazy chunk errors for good</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Tue, 27 Oct 2026 10:48:37 +0000</pubDate>
      <link>https://dev.to/talentopsystems/an-old-tab-meets-a-new-deploy-fixing-reactlazy-chunk-errors-for-good-3p27</link>
      <guid>https://dev.to/talentopsystems/an-old-tab-meets-a-new-deploy-fixing-reactlazy-chunk-errors-for-good-3p27</guid>
      <description>&lt;p&gt;Checked against official sources on October 26, 2026&lt;/p&gt;

&lt;p&gt;This bug hides in production. It never shows up on your laptop, and it only hits some people. We rebuilt it on our own machine, fixed it, and ran every piece of code below before writing a word about it.&lt;/p&gt;

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

&lt;p&gt;A developer on a React forum laid it out clearly. A user opens the app and leaves the tab open. The team ships a new version. Later, the user clicks a link to a page that loads with &lt;code&gt;React.lazy&lt;/code&gt;. The page breaks.&lt;/p&gt;

&lt;p&gt;The error says "Failed to fetch dynamically imported module" or &lt;code&gt;ChunkLoadError&lt;/code&gt;. In a fresh tab, the same page works fine. An error boundary can show a friendly message, but it cannot fix the tab. Trying the same import again fails again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it breaks
&lt;/h2&gt;

&lt;p&gt;Build tools like Vite give each built file a name with a hash in it, such as &lt;code&gt;Reports-D6ha3SoT.js&lt;/code&gt;. The hash changes when the code changes. That is great for caching. A browser can keep a hashed file for a long time, because new code always gets a new name. web.dev calls these "versioned URLs."&lt;/p&gt;

&lt;p&gt;Now think about the old tab. It still runs the old main file in memory. That old file only knows the old name, &lt;code&gt;Reports-D6ha3SoT.js&lt;/code&gt;. After the deploy, the server has &lt;code&gt;Reports-CEShBa1e.js&lt;/code&gt; instead. If the old file was deleted, the server answers 404, and the import fails.&lt;/p&gt;

&lt;p&gt;The Vite docs describe this exact case. When a host deletes the files from earlier deploys, a person who visited before the deploy "might encounter an import error."&lt;/p&gt;

&lt;p&gt;Two more facts make it worse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;import()&lt;/code&gt; returns a promise. If the file is missing, that promise rejects.&lt;/li&gt;
&lt;li&gt;React caches the promise behind &lt;code&gt;lazy&lt;/code&gt;. The React docs say React will not call the load function more than once. If the promise rejects, React throws the reason to the nearest error boundary. So the component stays broken until the page loads fresh code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why a retry does not help. The old tab just asks for the same old name again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our fix: three layers
&lt;/h2&gt;

&lt;p&gt;No single trick covers every case. We use three layers, from the server out to the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: the right cache headers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Send &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; on &lt;code&gt;index.html&lt;/code&gt;. MDN explains that &lt;code&gt;no-cache&lt;/code&gt; lets a cache store the file, but it must check with the server before each reuse. So a fresh page load always finds the newest entry file. The Vite docs ask for this same header on the HTML file.&lt;/li&gt;
&lt;li&gt;Send a long cache with &lt;code&gt;immutable&lt;/code&gt; on hashed files in &lt;code&gt;/assets/&lt;/code&gt;. Their names change when their content changes, so this is safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the tiny Node server we used to test these headers:&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="c1"&gt;// Tiny static server with the cache headers we recommend.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;normalize&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;4173&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;replace&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="p"&gt;,&lt;/span&gt; &lt;span class="dl"&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="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public, max-age=31536000, immutable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/octet-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not found&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serving&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 2: deploy in the right order, and keep old files for a while
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Upload the new files in &lt;code&gt;/assets/&lt;/code&gt; first.&lt;/li&gt;
&lt;li&gt;Upload &lt;code&gt;index.html&lt;/code&gt; last, so it never points at a file that is not there yet.&lt;/li&gt;
&lt;li&gt;Do not delete the old hashed files right away. Keep them for a set time, then clean them up with a script.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the old files still on the server, the old tab loads its old chunk, and nothing breaks. How long you keep them is your call. Think about how long your users leave tabs open.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: one safe reload in the browser
&lt;/h3&gt;

&lt;p&gt;Sometimes you must delete old files fast, for example after a security fix. Then the old tab needs fresh code, and the simple way to get it is a reload. The risk is a reload loop: if the file is still missing after the reload, the page would reload forever.&lt;/p&gt;

&lt;p&gt;So we keep a flag in &lt;code&gt;sessionStorage&lt;/code&gt;. MDN says &lt;code&gt;sessionStorage&lt;/code&gt; belongs to one tab and survives reloads. That is just what we need: reload once per tab, then stop and show the error.&lt;/p&gt;

&lt;p&gt;We kept the logic apart from React so it is easy to test.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/loadWithOneReload.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pure helper: no React here, so it is easy to test.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_ERROR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/Failed to fetch dynamically imported module|error loading dynamically imported module|Importing a module script failed|ChunkLoadError|Loading chunk &lt;/span&gt;&lt;span class="se"&gt;[\w&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+ failed/i&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isChunkError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_ERROR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadWithOneReload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;importer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;reload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`chunk-reload:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;importer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// it worked, so clear the flag&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isChunkError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="c1"&gt;// a real bug: show it&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="c1"&gt;// we already reloaded once: stop&lt;/span&gt;
      &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
      &lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="c1"&gt;// wait here while the page reloads&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;src/lazyWithReload.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loadWithOneReload&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./loadWithOneReload.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;lazyWithReload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;importer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;loadWithOneReload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;importer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;key&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;Using it looks like normal &lt;code&gt;lazy&lt;/code&gt;. Keep an error boundary around it for the case where the reload did not help:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Reports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazyWithReload&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Reports.jsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why we made these choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We only reload for chunk errors. A normal bug, like a typo in the module, is thrown as usual. A reload would only hide it.&lt;/li&gt;
&lt;li&gt;The flag is cleared after a good load. So if you deploy again tomorrow, the same tab can recover again.&lt;/li&gt;
&lt;li&gt;Browsers word this error in different ways. The pattern covers the common messages we know of, but it is a best effort, so keep it current.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use Vite, there is also a built-in hook. The Vite docs say Vite fires a &lt;code&gt;vite:preloadError&lt;/code&gt; event when it fails to load a dynamic import, and calling &lt;code&gt;event.preventDefault()&lt;/code&gt; stops the error from being thrown. Their example simply reloads the page. Our helper works without it, but you could use that event the same way, with the same one-reload flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we tested it
&lt;/h2&gt;

&lt;p&gt;Our setup: Node 20.19.2, Vite 8.3.5, React 19.3.1, the built-in &lt;code&gt;node:test&lt;/code&gt; runner, and Google Chrome (headless) driven by Playwright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Unit tests for the helper.&lt;/strong&gt; We passed in a fake storage and a fake reload, so the tests run in plain Node. Five tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first chunk error calls reload once, and the promise keeps waiting.&lt;/li&gt;
&lt;li&gt;A second chunk error, after the reload, throws. No loop.&lt;/li&gt;
&lt;li&gt;A good load clears the flag.&lt;/li&gt;
&lt;li&gt;A normal error (&lt;code&gt;ReferenceError&lt;/code&gt;) is thrown, with no reload.&lt;/li&gt;
&lt;li&gt;The pattern matches the common chunk error messages and skips a normal error.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node --test test/
# tests 5
# pass 5
# fail 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. A two-deploy test over HTTP.&lt;/strong&gt; We built the app twice, as build A and build B, with one change in the lazy page. Then we served build B two ways with the server above, and asked for build A's lazy file, like an old tab would:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bash deploy-test.sh
build A lazy chunk: Reports-D6ha3SoT.js
build B lazy chunk: Reports-CEShBa1e.js
build A entry file points at Reports-D6ha3SoT.js
clean deploy, old tab asks for Reports-D6ha3SoT.js: HTTP 404
keep-old deploy, old tab asks for Reports-D6ha3SoT.js: HTTP 200
new index.html points at: assets/index-C0CsCx8r.js
index.html header: Cache-Control: no-cache
asset header: Cache-Control: public, max-age=31536000, immutable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The clean deploy shows the bug: the old name is gone. The keep-old deploy fixes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A real browser.&lt;/strong&gt; We opened the app on build A in Chrome. With the tab still open, we deployed again. Then we clicked "Open reports." We counted page loads and read what the page showed. We ran three cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep-old deploy:&lt;/strong&gt; build A and build B files are both on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean deploy:&lt;/strong&gt; only build B files are on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunk still gone:&lt;/strong&gt; build B, but with the lazy file removed too, so even a reload cannot help.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python3 browser-test.py
keep-old deploy : page loads=1 (reloads=0), shows: 'Reports page, build A'
clean deploy    : page loads=2 (reloads=1), shows: 'Reports page, build B'
chunk still gone: page loads=2 (reloads=1), shows: 'This page could not load. Reload'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what we wanted. With old files kept, the user notices nothing. With a clean deploy, the tab reloads once and gets the new page. When the file is gone for good, the tab reloads once, then shows the error boundary. No loop.&lt;/p&gt;

&lt;p&gt;One honest detail: in our small demo, the lazy page opens from a button, not a URL. So after the reload, we had to click again. In a real app with a router, the URL already points at the lazy page, so the reload lands right on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trade-offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keeping old files costs a little storage.&lt;/strong&gt; It is also the smoothest fix, because users notice nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old files stay public while you keep them.&lt;/strong&gt; Anyone can still fetch old code at its old URL, including code you meant to remove. Keep them for a set time, not forever. If a secret ever got into a bundle, change the secret. Deleting the file is not enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A reload can lose work.&lt;/strong&gt; If the user typed into a form, a reload wipes it. This is why Layer 2 is our first defense, and the reload is the backup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matching error text is fragile.&lt;/strong&gt; Browser messages can change. Keep the pattern in one place, and test it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Another option is a version check.&lt;/strong&gt; The app asks the server for the current version and asks the user to reload when it is out of date. It is more work, but the user picks the moment to reload.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to check the fix on your own app
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;curl -I&lt;/code&gt; on your &lt;code&gt;index.html&lt;/code&gt; and on one file in &lt;code&gt;/assets/&lt;/code&gt;. You should see &lt;code&gt;no-cache&lt;/code&gt; on the first and a long &lt;code&gt;max-age&lt;/code&gt; on the second.&lt;/li&gt;
&lt;li&gt;Open your app in a tab. Deploy a change to a lazy page. Do not refresh.&lt;/li&gt;
&lt;li&gt;Click the link to that lazy page. With old files kept, it should just work. With old files deleted, you should see one reload, and then the new page.&lt;/li&gt;
&lt;li&gt;In the browser's dev tools, the Network panel should show the failed chunk request (a 404), then a fresh &lt;code&gt;index.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Delete the file on purpose and try again in the same tab. After one reload, you should see your error boundary, not an endless reload.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Docs we read
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vite docs, "Building for Production", the "Load Error Handling" part (on vite.dev)&lt;/li&gt;
&lt;li&gt;React, &lt;code&gt;lazy&lt;/code&gt;: &lt;a href="https://react.dev/reference/react/lazy" rel="noopener noreferrer"&gt;https://react.dev/reference/react/lazy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;React, &lt;code&gt;Component&lt;/code&gt; (error boundaries): &lt;a href="https://react.dev/reference/react/Component" rel="noopener noreferrer"&gt;https://react.dev/reference/react/Component&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN, &lt;code&gt;import()&lt;/code&gt;: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN, &lt;code&gt;Cache-Control&lt;/code&gt;: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN, &lt;code&gt;Window.sessionStorage&lt;/code&gt;: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN, &lt;code&gt;Location.reload()&lt;/code&gt;: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Location/reload" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Location/reload&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;web.dev, "Prevent unnecessary network requests with the HTTP Cache": &lt;a href="https://web.dev/articles/http-cache" rel="noopener noreferrer"&gt;https://web.dev/articles/http-cache&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  From TalentOp Systems LLC
&lt;/h2&gt;

&lt;p&gt;We are TalentOp Systems LLC, based in Omaha, Nebraska. We started in February 2025. We work in IT staffing, IT software consulting, project development, and manpower and staffing solutions for clients across the United States. Website: &lt;a href="http://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt;. Email: &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>vite</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What E-Verify means for a new hire, in plain words</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Sun, 25 Oct 2026 17:16:32 +0000</pubDate>
      <link>https://dev.to/talentopsystems/what-e-verify-means-for-a-new-hire-in-plain-words-4o95</link>
      <guid>https://dev.to/talentopsystems/what-e-verify-means-for-a-new-hire-in-plain-words-4o95</guid>
      <description>&lt;p&gt;&lt;em&gt;Written from the official pages we read on October 26, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We use E-Verify at TalentOp Systems LLC, and people often ask us what that means for them. Here are our plain answers, in short parts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A company that uses E-Verify checks whether every new hire has the right to work in the United States. That is the whole job of the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What E-Verify is
&lt;/h2&gt;

&lt;p&gt;E-Verify is a free online system from the U.S. government. Two agencies run it together: USCIS (U.S. Citizenship and Immigration Services) and the Social Security Administration (SSA).&lt;/p&gt;

&lt;h2&gt;
  
  
  How a check works
&lt;/h2&gt;

&lt;p&gt;E-Verify works with Form I-9. Every employer must use Form I-9 to check a new worker's identity and right to work.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The new hire fills out Form I-9.&lt;/li&gt;
&lt;li&gt;The employer types details from that form into E-Verify.&lt;/li&gt;
&lt;li&gt;E-Verify compares the details with records held by the Department of Homeland Security (DHS) and SSA.&lt;/li&gt;
&lt;li&gt;The employer usually gets an answer in a few seconds.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When the check happens
&lt;/h2&gt;

&lt;p&gt;The check comes only after a person accepts a job offer and fills out Form I-9. A company may not run an E-Verify check on someone before that point.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the records do not match
&lt;/h2&gt;

&lt;p&gt;Now and then, the details do not match the records. The worker can take action to fix this. The e-verify.gov site explains what to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to tell if a company uses E-Verify
&lt;/h2&gt;

&lt;p&gt;There are two easy ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The notice.&lt;/strong&gt; Companies that use E-Verify must post an E-Verify participation notice where they hire, or share it with job application papers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The search tool.&lt;/strong&gt; The E-Verify Employer Search on e-verify.gov lists employers that are enrolled. Each name comes from what the employer gave when it signed up. So search for both the legal name and the public name.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What E-Verify does not tell you
&lt;/h2&gt;

&lt;p&gt;E-Verify only confirms that a new hire can work in the U.S.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is not a background check.&lt;/li&gt;
&lt;li&gt;It does not test skills or job requirements.&lt;/li&gt;
&lt;li&gt;It does not show whether a company sponsors visas. To learn that, ask the company directly. You can ask us at &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A note before you go
&lt;/h2&gt;

&lt;p&gt;We share this as general background. TalentOp Systems LLC is not giving legal advice here. If you have a question about your own case, please talk it over with a licensed immigration attorney.&lt;/p&gt;

&lt;h2&gt;
  
  
  About us
&lt;/h2&gt;

&lt;p&gt;We are TalentOp Systems LLC, an IT staffing and software consulting firm based in Omaha, Nebraska. We opened in February 2025 and serve clients across the United States. Every new hire at our firm goes through an E-Verify check.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt; · Email: &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Official pages cited
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.e-verify.gov/about-e-verify" rel="noopener noreferrer"&gt;https://www.e-verify.gov/about-e-verify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.e-verify.gov/about-e-verify/what-is-e-verify" rel="noopener noreferrer"&gt;https://www.e-verify.gov/about-e-verify/what-is-e-verify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.e-verify.gov/employees/e-verify-overview" rel="noopener noreferrer"&gt;https://www.e-verify.gov/employees/e-verify-overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.e-verify.gov/e-verify-employer-search" rel="noopener noreferrer"&gt;https://www.e-verify.gov/e-verify-employer-search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.e-verify.gov/facts" rel="noopener noreferrer"&gt;https://www.e-verify.gov/facts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.e-verify.gov/employees/tentative-nonconfirmation-mismatch-overview" rel="noopener noreferrer"&gt;https://www.e-verify.gov/employees/tentative-nonconfirmation-mismatch-overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.e-verify.gov/about-e-verify/history-and-milestones" rel="noopener noreferrer"&gt;https://www.e-verify.gov/about-e-verify/history-and-milestones&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>hiring</category>
      <category>jobs</category>
      <category>immigration</category>
    </item>
    <item>
      <title>What IT software consulting means, in plain words</title>
      <dc:creator>TalentOp Systems LLC</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:44:04 +0000</pubDate>
      <link>https://dev.to/talentopsystems/what-it-software-consulting-means-in-plain-words-4kko</link>
      <guid>https://dev.to/talentopsystems/what-it-software-consulting-means-in-plain-words-4kko</guid>
      <description>&lt;p&gt;&lt;em&gt;Written on October 23, 2026, by TalentOp Systems LLC.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;People hear "IT consulting" all the time. But what does it mean? Here is a plain answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A company has a tech problem. It does not have the right people to fix it, or it does not have enough time. So it brings in outside experts for a while. Those experts are IT consultants.&lt;/p&gt;

&lt;h2&gt;
  
  
  What consultants do
&lt;/h2&gt;

&lt;p&gt;Consultants look at what a company has today. Then they help it decide what to build or change. Often they also do the work. They may:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look at an old system and find what slows it down.&lt;/li&gt;
&lt;li&gt;Plan a new app or tool.&lt;/li&gt;
&lt;li&gt;Write and test the code.&lt;/li&gt;
&lt;li&gt;Move data from an old system to a new one.&lt;/li&gt;
&lt;li&gt;Teach the company's own team how to run the new system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Consulting, staffing and project work: what is the difference?
&lt;/h2&gt;

&lt;p&gt;These three words often show up together. They are not the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IT staffing&lt;/strong&gt; means a company needs a person with certain tech skills. A staffing firm finds that person. The person joins the client's team and works there, often for months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IT software consulting&lt;/strong&gt; means the client wants expert advice and help on a tech problem. The consultant brings both the skills and a plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project development&lt;/strong&gt; means building one thing from start to finish, like a new app. The project has a clear goal and an end.&lt;/p&gt;

&lt;p&gt;Many firms do all three. The right fit depends on what the client needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who uses consultants?
&lt;/h2&gt;

&lt;p&gt;Almost every kind of business does. Banks, insurance companies, hospitals and stores all run on software. When that software has to grow or change, they often call in help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters if you work in tech
&lt;/h2&gt;

&lt;p&gt;If you are a developer, tester or data person, consulting work can put you on many kinds of projects. You see how different companies solve problems. Many consulting roles are contracts. Some last a year or more.&lt;/p&gt;

&lt;h2&gt;
  
  
  About TalentOp Systems LLC
&lt;/h2&gt;

&lt;p&gt;TalentOp Systems LLC is an IT staffing and software consulting company with an office in Omaha, Nebraska. It started in February 2025 and works with clients across the United States. Its services are IT staffing, IT software consulting, project development, and manpower and staffing solutions. Its clients are in banking, insurance, healthcare and retail. The company is E-Verified.&lt;/p&gt;

&lt;p&gt;To learn more, visit &lt;a href="https://www.talentopsystems.com" rel="noopener noreferrer"&gt;www.talentopsystems.com&lt;/a&gt; or write to &lt;a href="mailto:info@talentopsystems.com"&gt;info@talentopsystems.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>consulting</category>
      <category>software</category>
      <category>hiring</category>
    </item>
  </channel>
</rss>
