<?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: casper__</title>
    <description>The latest articles on DEV Community by casper__ (@earthjan).</description>
    <link>https://dev.to/earthjan</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%2F457739%2F60ee9485-e2b7-4680-b0e0-edd83b719ebb.png</url>
      <title>DEV Community: casper__</title>
      <link>https://dev.to/earthjan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/earthjan"/>
    <language>en</language>
    <item>
      <title>Our tests now run 2-4x faster, and cost about $0.09 a run, down from $0.17-0.34</title>
      <dc:creator>casper__</dc:creator>
      <pubDate>Thu, 22 Oct 2026 09:51:36 +0000</pubDate>
      <link>https://dev.to/earthjan/our-tests-now-run-2-4x-faster-and-cost-about-009-a-run-down-from-017-034-5h4k</link>
      <guid>https://dev.to/earthjan/our-tests-now-run-2-4x-faster-and-cost-about-009-a-run-down-from-017-034-5h4k</guid>
      <description>&lt;p&gt;&lt;strong&gt;The result in one table.&lt;/strong&gt; Same tests, same results, measured against where we started.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Per code change&lt;/th&gt;
&lt;th&gt;Before (one old job)&lt;/th&gt;
&lt;th&gt;Now (one machine)&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wait&lt;/td&gt;
&lt;td&gt;29–57 min&lt;/td&gt;
&lt;td&gt;about 15 min&lt;/td&gt;
&lt;td&gt;48–74% less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Machine time&lt;/td&gt;
&lt;td&gt;29–57 min&lt;/td&gt;
&lt;td&gt;about 15 min&lt;/td&gt;
&lt;td&gt;48–74% less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Estimated cost&lt;/td&gt;
&lt;td&gt;$0.17–0.34&lt;/td&gt;
&lt;td&gt;about $0.09&lt;/td&gt;
&lt;td&gt;48–74% less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Estimated cost over 100 runs&lt;/td&gt;
&lt;td&gt;$17–34&lt;/td&gt;
&lt;td&gt;about $9&lt;/td&gt;
&lt;td&gt;$8–25 saved&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cost rows are estimates: job minutes times GitHub's published rate, not our bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Our automated tests took 29 to 57 minutes on every code change. Now they take about 15, on a single machine (48–74% less). Three changes did it. First, I split the tests into six groups that run at the same time. That cut the wait to 15.1 minutes (48–74% less), but used more machine time (78 minutes per run, 37–169% more). Second, I found one import that made every test file load about 10,000 files it never used, and replaced it. That cut the wait to 7.9 minutes (48% less than six groups) and the machine time to 44 minutes per run (44% less). Third, I came back to an idea I had first set aside, sharing test workers, fixed what it broke, and folded the six groups back into one job. That takes about 15 minutes of one machine per run (66% less than the 44 before). At GitHub's published rate of $0.006 a minute, that is about 9 cents a run, against 17 to 34 cents for the old single job (48–74% less). These are estimates from job lengths, not our bill. This post covers where the time went, what broke along the way, the idea I set aside and later shipped, and what is left to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5s01ekt51wbf7n13ydd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5s01ekt51wbf7n13ydd.png" alt="Where one run time went: about 3,800 s, measured on a quiet machine. 66% loading, 34% testing" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem is that heavy tests make every run slow
&lt;/h2&gt;

&lt;p&gt;Our admin web app has about &lt;strong&gt;7,000 test cases, spread over 960 test files.&lt;/strong&gt; Every code change runs them on CI, the automated checks that run on each change. All of them ran on one small machine with 2 processor cores, one after another.&lt;/p&gt;

&lt;p&gt;That took 29 to 57 minutes. Every code change waits that long. The tests are heavy. Most of the run goes to loading the app, not testing it.&lt;/p&gt;

&lt;p&gt;A side effect. On a busy machine, some tests timed out and failed for no real reason. The fix below solved that too.&lt;/p&gt;

&lt;h2&gt;
  
  
  A first idea was to run six groups at the same time
&lt;/h2&gt;

&lt;p&gt;Split the 960 files into six groups. Run all six at once, each on its own machine. The test runner calls a group a shard. A final job waits for all six and reports one answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg56p0f0t6g2ea3ezgrbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg56p0f0t6g2ea3ezgrbo.png" alt="Before and after: 1 job split into 6 groups" width="799" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same tests, six machines instead of one. I also made every test clean up after itself (fake parts, shared variables, timers), so no test can affect the next one.&lt;/p&gt;

&lt;p&gt;The first result was 15.1 minutes, down from 29 to 57 (a cut of 48% to 74%). The trade was more machine time. Each group still pays the loading cost, so charged minutes per run went from 29–57 to 78 (37% to 169% more). A later fix, covered below, brought that down to 44 (44% less) and the wait down to 7.9 minutes (48% less).&lt;/p&gt;

&lt;h2&gt;
  
  
  The time went on getting ready, not testing
&lt;/h2&gt;

&lt;p&gt;Before splitting anything, I measured. Loading the app's code before each test file was the biggest cost, at about 2,500 s of 3,800 s in total (66%), on a quiet machine. So most of the run was not testing. It was getting ready to test.&lt;/p&gt;

&lt;p&gt;CI proves it also. One group took 786 s. Loading took 445 s of it (57%). The tests took 90 s (11%). Everything else was overhead.&lt;/p&gt;

&lt;p&gt;The loading has a culprit. It is react-admin, the library our admin screens are built on. Loading it pulls in about 12,800 code files and takes about 5 s. Any test that touches it pays that price. I first blamed MUI, the UI component library. Wrong. I blamed the wrong library, confidently. MUI comes along for the ride, because react-admin loads all of it. Trimming MUI imports could not reach even a 10% cut.&lt;/p&gt;

&lt;h2&gt;
  
  
  I set aside sharing the test workers, then came back to it
&lt;/h2&gt;

&lt;p&gt;By default, the test runner (Vitest) starts every test file in a fresh worker, a separate copy of the program. So the app code loads again for every file. The obvious fix is to share workers and load once.&lt;/p&gt;

&lt;p&gt;I tried it. Loading dropped from 2,500 s to 131 s. That is −95%.&lt;/p&gt;

&lt;p&gt;Then 211 files failed. One test leaves a fake part or a global value behind, and the next test trips over it. The suite had been leaning on fresh workers to hide that. I set it aside for this round.&lt;/p&gt;

&lt;p&gt;I needed a sure win now, one I could prove with confidence. Sharing workers saves the most, but I had only a partial test of it, and 211 files must be fixed before I can trust it. The icon fix was one change I could measure in the real pipeline. So I shipped the sure one first, and I came back to sharing workers afterwards, below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cleanup that came with six groups
&lt;/h2&gt;

&lt;p&gt;The cleanup step broke 8 files (63 tests) at first, because they relied on leftovers from earlier tests. I fixed it in five files of fake parts and one test. Boring, and necessary. The shared-workers trial pointed at the same problem.&lt;/p&gt;

&lt;p&gt;Running the tests on my laptop barely changed. The median was 352 s against 340 s before, under my 374 s limit. The three runs ranged from 313 to 541 s on a busy machine, so call it close, not proven.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I found the real culprit, and the wait halved again
&lt;/h2&gt;

&lt;p&gt;Back to the library that loads 12,800 files. I counted them by package. 10,612 are icon files from MUI's icon set. One line in react-admin asks for the whole icon set so it can use two icons. Every test file paid for all of them.&lt;/p&gt;

&lt;p&gt;The fix is a stand-in that loads each icon only when a test asks for it. Tests still get the real icons. Nothing else changes. On a sample of 25 test files, loading fell from 99.9 s to 24.5 s (−75%), and all 230 tests still passed.&lt;/p&gt;

&lt;p&gt;In CI the same six groups went from 9–15 minutes each to 5–8. The slowest group fell from 15.0 to 7.9 minutes. Charged minutes per run fell from 74 to 44, which is about what the old single job cost.&lt;/p&gt;

&lt;p&gt;Then I tried the obvious next step: one machine, no groups. It hit the 30-minute limit and was cancelled. The six groups add up to about 42 machine-minutes, and one machine needs those 42 minutes in a row. So the groups stayed, until loading got cheap in another way (next section).&lt;/p&gt;

&lt;p&gt;I also looked for more imports like it. One more exists. It is a date library, pulled in by a react-admin feature we don't use. Replacing it gains 9% on the sample, about one or two charged minutes. Not worth the extra moving part, so I left it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I came back to sharing workers, and one machine was enough
&lt;/h2&gt;

&lt;p&gt;The icon fix made loading cheap, but every test file still started a fresh worker. So I went back to the idea I had set aside: reuse workers, so the app loads once per worker instead of once per file. This time I fixed what broke before turning it on.&lt;/p&gt;

&lt;p&gt;Sharing workers shows every leftover a test leaves behind. These turned up, one by one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The style cache stayed tied to the first file's page.&lt;/strong&gt; MUI draws its styles through a library called Emotion. It is like pinning your notes to the wall of a room that was then torn down: later files drew into a page that no longer existed. Now there is one copy of the library, and each file gets a fresh cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The click-and-type helper remembered the first file's document.&lt;/strong&gt; It now gets the current one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The pop-up message library kept a queue between files.&lt;/strong&gt; It is emptied before each file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React Query remembered the first file's window.&lt;/strong&gt; It now points at the current one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some test helpers left an app mounted after the file ended.&lt;/strong&gt; They are now unmounted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two real bugs in the app itself.&lt;/strong&gt; Both were lists missing a React &lt;code&gt;key&lt;/code&gt;. One made an expired-invitation chip warn on every draw. The other let an open tooltip jump to the next attachment after you deleted the first. I fixed both, each with a failing test first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then the real pipeline. The first set of runs failed: 159 of 990 files. A leftover app fired a notification when its page no longer existed. The error stopped React from resetting its own state in that worker, so every later draw in it failed with "Should not already be working." It did not happen on my laptop, even with the same random order, because my laptop is too fast for the race. I did not retry. I removed the leftover and started three fresh runs.&lt;/p&gt;

&lt;p&gt;All three passed: 990 of 990 files, 7,438 tests passed and 1 skipped, in 14 min 20 s, 15 min 52 s and 14 min 14 s. I then switched the pipeline to the one machine, and the three runs on that setup took 13 min 5 s, 14 min 59 s and 17 min 3 s.&lt;/p&gt;

&lt;p&gt;To keep it honest, test files now run in a random order, and every run prints its seed. Tests inside a file keep their written order. A new test that leaks fails loudly instead of passing thanks to its neighbours, and the seed lets me replay the exact order.&lt;/p&gt;

&lt;p&gt;One caution. The spread is wide, from 13 to 17 minutes, and the median sits right at my 15 minute goal. If the suite grows, the wait will pass 15 first. My rule is to open a ticket if two runs in a row push the median past 15 minutes, or any run goes past about 22.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clock time fell about 2–4x, cost fell by about half or more, to roughly 9 cents a run, and two bars missed at first
&lt;/h2&gt;

&lt;p&gt;The wait went from 29–57 minutes to 15.1 with six groups, then to 7.9 after the icon fix. My goal was 15. The final setup, one machine with shared workers, took 14 min 59 s at the median of three runs, and 13 min 5 s to 17 min 3 s across them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F201ol3ze5x2m5cqzssju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F201ol3ze5x2m5cqzssju.png" alt="CI run times: the old one-job range, then the last run of each setup, plus the icon fix" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5gl2et9m9o3qqz9lbhtv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5gl2et9m9o3qqz9lbhtv.png" alt="CI job lengths and estimated cost: old single job, six groups, six groups plus icon fix, and the delivered one machine" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I set five bars. The six-group run before the icon fix, against the other four (the next lines give the newer numbers).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bar&lt;/th&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Timeout failures, 3 runs&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Met&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minutes charged per run&lt;/td&gt;
&lt;td&gt;80 or less&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;Met&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local test run, median&lt;/td&gt;
&lt;td&gt;374 s or less&lt;/td&gt;
&lt;td&gt;352 s&lt;/td&gt;
&lt;td&gt;Met&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slowest ÷ fastest group&lt;/td&gt;
&lt;td&gt;1.5 or less&lt;/td&gt;
&lt;td&gt;1.7&lt;/td&gt;
&lt;td&gt;Missed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One machine changes the bars. Balance no longer applies, because there is one group. The others hold: no timeout failures in the six green full runs, about 15 charged minutes (about 9 cents) against a limit of 80, and a median wait of 14 min 59 s against 15. I read charged minutes from job lengths, not from the bill, and I did not measure the local run again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two bars missed at first, and the thresholds are only my guesses
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clock time&lt;/strong&gt; was 15.1 min against a goal of 15. Close, not a pass. After the icon fix it was 7.9 min, met. On one machine it is 14 min 59 s at the median, with runs from 13 min 5 s to 17 min 3 s: met, with almost no room.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balance&lt;/strong&gt; was 1.7 (slowest ÷ fastest group) against my 1.5 bar. It was 1.4 the run before. The split is uneven and moves between runs. After the icon fix it was 1.5 (475 s over 320 s), which meets the bar. On one machine there is a single group, so this bar no longer applies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The thresholds are mine.&lt;/strong&gt; 15 min, 80 charged minutes, 1.5×. I picked them. They are a call between speed and spend, not a law.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The price is an estimate.&lt;/strong&gt; GitHub publishes the rate for our runner: $0.006 per minute for a Linux 2-core machine, rounded up to whole minutes per job. I did not read our bill, so the real figure may differ. At that rate, the old single job's 29 to 57 minutes cost about $0.17 to $0.34, six groups' 78 about $0.47, the 44 after the icon fix about $0.26, and the one machine's 15 about $0.09. The lint check and the final gate add about $0.02 to each.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the numbers come from
&lt;/h2&gt;

&lt;p&gt;Timings come from my own runs on a laptop and from the run logs of our CI pipeline (GitHub Actions, with Vitest as the test runner). The old 29–57 minute range is from our CI history before the change. The one-machine times are the job lengths GitHub reports, and the test counts come from the run logs. The dollar figures use GitHub's published runner rate (docs.github.com/en/billing/reference/actions-runner-pricing), applied to job lengths.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do again
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measure first.&lt;/strong&gt; I blamed the wrong library until the numbers said otherwise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No retries.&lt;/strong&gt; A flaky test should look flaky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean up after every test.&lt;/strong&gt; It is the price of running them side by side, and sharing workers found every one I had missed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up CI on a cold machine early.&lt;/strong&gt; It found problems my laptop missed. The failure in the first try at sharing workers never showed up there, even with the same seed.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>githubactions</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stoic in Modern Times</title>
      <dc:creator>casper__</dc:creator>
      <pubDate>Mon, 12 Oct 2026 12:01:42 +0000</pubDate>
      <link>https://dev.to/earthjan/stoic-in-modern-times-5cjg</link>
      <guid>https://dev.to/earthjan/stoic-in-modern-times-5cjg</guid>
      <description>&lt;p&gt;&lt;em&gt;My take on Stoicism: where it matches my principles, and where I push back&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;TLDR: Many Stoic ideas align with principles I already have. Where they don't, I push back, especially on consistency and on indifference, the Stoic view that externals are "indifferent".&lt;/p&gt;

&lt;p&gt;Stoicism's goal is simple. Strive to be virtuous.&lt;/p&gt;

&lt;p&gt;So, this is how Stoicism lines up with my own experience and principles, as a person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four virtues, one ruler
&lt;/h2&gt;

&lt;p&gt;Stoics break virtue into four parts. Here's how I understand each, and what it looks like in modern work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwj3cfeuf1z7dhbqxsv4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbwj3cfeuf1z7dhbqxsv4.png" alt="The four virtues hold up one goal: to be virtuous" width="799" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wisdom.&lt;/strong&gt; Clear perception and sound judgment about what matters and how to act. Ask yourself: what is actually true and appropriate here? It's choosing the simple path instead of overcomplicating things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Courage.&lt;/strong&gt; Acting rightly despite fear, discomfort, or uncertainty. Ask yourself: am I avoiding the right action because it's uncomfortable? It's giving honest feedback, or owning a mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Justice.&lt;/strong&gt; Integrity and fairness toward others, and contributing to the common good. Ask yourself: is this fair, honest, and respectful to others? It's not taking credit for others' work, and treating everyone with respect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Temperance.&lt;/strong&gt; Self-mastery over impulses so reason stays in control. Ask yourself: am I driven by impulse, ego, or excess? It's staying calm during criticism, and not overworking until burnout.&lt;/p&gt;

&lt;p&gt;Stoics measure a good life by these four. That's their "ruler". But, I have my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure yourself with your own ruler
&lt;/h2&gt;

&lt;p&gt;Measuring yourself with your own ruler gives you freedom. Pursue what truly matters to you, without the burden of external comparison.&lt;/p&gt;

&lt;p&gt;So, who am I? Here's my ruler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Striving for financial stability and security for my family. Having choices. Not compromising.&lt;/li&gt;
&lt;li&gt;Providing value to society.&lt;/li&gt;
&lt;li&gt;Encouraging fairness and justice.&lt;/li&gt;
&lt;li&gt;Aiming for skill and self-mastery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The way I see it: wealth creates choice, choice gives time, and time gives happiness.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz8l7vhgwma7wwdl0apl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz8l7vhgwma7wwdl0apl9.png" alt="My ruler: wealth creates choice, choice gives time, time gives happiness" width="799" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also plan my life down to the lowest level, which is my day. This means I'm leading myself, not being led by others. I control what to prioritize.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where I don't fully agree
&lt;/h3&gt;

&lt;p&gt;Stoics say the only true measure of a good life is virtue. External things like health, status, and even length of life are "indifferent". They're not truly good or evil on their own, so using them as measures is misguided. Instead, measure how wise, just, courageous, and temperate you have been.&lt;/p&gt;

&lt;p&gt;I don't get this one. For me, treating things like my family's financial security as "indifferent" sounds like compromise. Even careless.&lt;/p&gt;

&lt;p&gt;But, Stoics also have what they call "preferred indifference". Things like wealth and health are fine to pursue, as long as you don't trade virtue for them. That softens my disagreement. Basically, pursue security for your family, but never by compromising virtue.&lt;/p&gt;

&lt;p&gt;Another thing. Measuring with your own ruler makes it easy to absolutely not consider other people's rulers. But your ruler won't always be fully developed. There's always room to improve. So, explore and remain open-minded, as long as it encourages virtue.&lt;/p&gt;

&lt;p&gt;What I do agree with: limit your exposure to environments that value comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work is a subset of life
&lt;/h2&gt;

&lt;p&gt;Life has many subsets, and work is one of them. Taking a real break, spending time with your family, doing what you love outside work. Those are life's other subsets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmnt8jbbneqmionyq4hu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmnt8jbbneqmionyq4hu9.png" alt="Work is one subset of life, not the whole of it" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, &lt;code&gt;life !== work&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If it's working hours, work hard. If it's a break, take it intentionally.&lt;/p&gt;

&lt;p&gt;Do you live to work? Or, do you work to live? This one is profound for me. Make money to buy time. That's why you work hard and get a good job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus creates time
&lt;/h2&gt;

&lt;p&gt;Think you need more time? Maybe you're not focusing well. No deep focus? Maybe your priorities are incorrect. Deep focus is basically what Cal Newport calls deep work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjewbgfgt4bnit2k7u3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgjewbgfgt4bnit2k7u3f.png" alt="Scattered day versus focused day" width="799" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your work hours are scattered. Remember, the brain needs 40 to 60 minutes to get into flow. But you get distracted with other tasks, and not just scrolling. With that, productivity is messed up. You try to compensate by taking overtime (OT). Two hours of work becomes sixteen.&lt;/p&gt;

&lt;p&gt;Focus = meaningful breaks. Work hard in working hours, so your break is an actual break.&lt;/p&gt;

&lt;p&gt;So, what do you focus on? Important things. And what are these important things? It's your own definition, based on your life's goal. That's your ruler again. Focus also relates to building a system where you can focus on the individual steps. Hence, discipline. I'll get back to that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act, don't just keep in you head
&lt;/h2&gt;

&lt;p&gt;Actions project your future, not knowledge and intention.&lt;/p&gt;

&lt;p&gt;Be aware. Reason out, then act accordingly. Be solution-oriented. Something is wrong? Ask. Be pragmatic. Live deliberately. But, above all, do it with wisdom and virtue.&lt;/p&gt;

&lt;p&gt;How to act? Follow a simple cycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do it now.&lt;/strong&gt; This is the most important. Don't think about perfection. You'll make mistakes. Don't overthink, because thinking leads to procrastination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do it right.&lt;/strong&gt; Then try to improve. Organize. Ask what went wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do it better.&lt;/strong&gt; Master it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcdw4mzvu53vtik3k5f6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcdw4mzvu53vtik3k5f6n.png" alt="Do it now, do it right, do it better, then repeat" width="799" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It applies to basically everything. Life. Chores. Career. Tasks. In short: make it work → improve → make it better.&lt;/p&gt;

&lt;p&gt;James Clear has a line that fits: "Find a way to play the game that ensures you get a lot of shots." This prevents you from putting all your effort and fortune into one chance. It promotes gradual improvement. It also lets you be strategic. You design your environment in a way you can recover from failures, hence growing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discipline is a system
&lt;/h2&gt;

&lt;p&gt;Discipline is freedom. You have control of yourself, and that is joyful and meaningful in itself.&lt;/p&gt;

&lt;p&gt;Now, discipline isn't in our DNA. So, organize your environment. Make it easier to do things. Want to exercise? Schedule it. Put the equipment in the same spot. Make it accessible and easy to wear.&lt;/p&gt;

&lt;p&gt;Motivation is the byproduct of discipline, not the spark to start acting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furls704qy7wg7phyhep6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furls704qy7wg7phyhep6.png" alt="Environment leads to discipline, which leads to motivation" width="799" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me, discipline is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Continuity.&lt;/strong&gt; One chain broken? Continue forging the next.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A process or system,&lt;/strong&gt; not an endpoint.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The willingness to learn.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where I push back
&lt;/h3&gt;

&lt;p&gt;A common take on discipline is to stay consistent, even if it's just doing enough. For example, it's better to exercise moderately for years than intensely for a month then quit.&lt;/p&gt;

&lt;p&gt;But, what if you aim to be disciplined in the standard you want? Then, create a system that lets you be consistent with it, especially on hard days. Take exercise. Why not exercise based on what you need than just consistency? If you need to build muscle in the most efficient way, that requires hard-core practice. Create a system for that. Then apply the same cycle: make it work → improve → make it better.&lt;/p&gt;

&lt;p&gt;For me, consistency = aiming to improve. Made a mistake? Yield to improvement instead of self-sabotage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calm confidence vs ego
&lt;/h2&gt;

&lt;p&gt;Calm confidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Has a growth mindset.&lt;/li&gt;
&lt;li&gt;Doesn't aim to dominate.&lt;/li&gt;
&lt;li&gt;Knows all actions are for improving.&lt;/li&gt;
&lt;li&gt;Tends to listen more.&lt;/li&gt;
&lt;li&gt;Isn't hungry for validation. It's grounded in principle and virtue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ego is the opposite. It's unstable and constantly needs validation. Got critique? Ego views it as an attack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftqcuo1t96ozrnado3pnb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftqcuo1t96ozrnado3pnb.png" alt="Calm confidence compared with ego" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, be confident and clear when speaking as if you're right, and listen as if you're wrong.&lt;/p&gt;

&lt;p&gt;Why speak like you're right? Because a Stoic acts on reason. You express an idea to enlighten, hence you articulate it properly. This also takes courage.&lt;/p&gt;

&lt;p&gt;Why listen like you're wrong? Because truth matters more than pride, and listening this way requires emotional discipline. If you feel defensive and impatient with an opposing view, pause. Then tell yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I value truth over my pride. I can be wrong. Let me hear this out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I learned a version of this in June 2024, at a Theoretical Driving Course. When I enrolled, they already took my personal information, including my full address. Then for the attendance, I was asked to give my address again.&lt;/p&gt;

&lt;p&gt;I didn't. I'm careful with my personal information. As you'd expect, the instructor's attention went to me. We had a light and short argument, and the whole class heard it. After that, he kept pointing it out just to prove his point. On the last day, I gave up and provided my address.&lt;/p&gt;

&lt;p&gt;What I learned: reason out your belief before acting on it. Question it. Prove it wrong. When you've built that foundation, no one, not even any situation, can easily shake it. Of course, don't be arrogant. Understand people's point of view, and see if they prove your belief wrong. That's how you improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure is for growth, not for glorifying
&lt;/h2&gt;

&lt;p&gt;Failure = growth. Mourn. Feel disappointed. But, eventually, analyze it to learn.&lt;/p&gt;

&lt;p&gt;Important note: Stoicism teaches you to be resilient, not to glorify failure.&lt;/p&gt;

&lt;p&gt;Focusing on solutions than problems is easier to say than do. Before you can learn from a failure, you need to breathe. When you mess up, you're likely too emotional to think. You may believe you're acting rationally, but in reality, emotion has taken over.&lt;/p&gt;

&lt;p&gt;It might sound silly, but I realized this deep within me through Minecraft. I play it to allow me to think. I had my most valuable tools: top enchants on my sword, pickaxe, and bow. I died in the Nether. I was trying to calm myself, but I didn't give myself even a brief moment. So, I went back for the tools and ended up losing more resources, like tons of iron. I died multiple times until I realized I was just acting on emotion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb9tm3aaz0cryxep2rs8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb9tm3aaz0cryxep2rs8u.png" alt="Two paths after losing my tools in Minecraft" width="799" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, don't cry over spilled milk. Figure out how not to spill next time, or how to clean it up better.&lt;/p&gt;

&lt;p&gt;A few more things I hold on to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seek experience, then reflect.&lt;/strong&gt; Without reflection, it's just a situation that went by. When you reflect, you gain wisdom, which naturally gives you confidence (courage), likely leading to success. But, confidence ≠ success automatically. There are setbacks and unfortunate events. Despite this, upholding your virtue in handling them is the success itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success and failure aren't black and white.&lt;/strong&gt; You failed? Not the end of the world. You succeeded? Only in that context, not as a whole, especially if you ignored virtue. Evaluate with nuance and focus on continuing progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Define your goal first.&lt;/strong&gt; To avoid stalling, assess if it's actually what you want. Your goal is where your assessments come from. Just because you feel discomfort in something doesn't mean you want to grow in it. That's naive. Be intentional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grow internally.&lt;/strong&gt; Stoicism targets internal growth, not external things like output and numbers. Internal growth is life's fundamental. Get it right, and whatever you do, you'll do it right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change it, accept it, or leave
&lt;/h2&gt;

&lt;p&gt;Have a positive outlook in any circumstance, whether it's positive or not. For me, a positive outlook is basically one aligned with growth.&lt;/p&gt;

&lt;p&gt;Naval Ravikant's framework fits here. Change what's within your control. Accept what isn't. Or remove yourself from the situation if neither serves you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5bbcmc41oft0ihvnhlv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5bbcmc41oft0ihvnhlv.png" alt="Change it, accept it, or leave" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the saying goes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't complain about the snow on your neighbor's roof when your own doorstep is unclean.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meaning, if you won't act on something fixable, you lose the right to complain about it.&lt;/p&gt;

&lt;p&gt;How do you decide between the three? Base it on your virtue, what matters to you. If it's outside of that, leave it or ignore it. Either way, the point is to act. Make the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still figuring out
&lt;/h2&gt;

&lt;p&gt;There's also the idea of "unbecoming": letting go of the excess to find your true self. How? I don't have the whole answer yet. But I believe aiming for Stoic virtue will help me remove the excess. Part of being virtuous is not letting other people's opinions shape what I want.&lt;/p&gt;

&lt;p&gt;I'll close with a Stoic line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't teach your philosophy. Embody it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Some of these ideas came from reading Addy Osmani's &lt;em&gt;The Stoic Mind&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
