<?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: FF</title>
    <description>The latest articles on DEV Community by FF (@fu_liang_dfbec863ed6c21e3).</description>
    <link>https://dev.to/fu_liang_dfbec863ed6c21e3</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F99mvlsfu5tfj9m7ku25d.png</url>
      <title>DEV Community: FF</title>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fu_liang_dfbec863ed6c21e3"/>
    <language>en</language>
    <item>
      <title>Buying the dip without catching the knife: a 2-line crypto pullback rule, walk-forward validated across 40 coins</title>
      <dc:creator>FF</dc:creator>
      <pubDate>Sun, 25 Oct 2026 14:38:27 +0000</pubDate>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3/buying-the-dip-without-catching-the-knife-a-2-line-crypto-pullback-rule-walk-forward-validated-3g5p</link>
      <guid>https://dev.to/fu_liang_dfbec863ed6c21e3/buying-the-dip-without-catching-the-knife-a-2-line-crypto-pullback-rule-walk-forward-validated-3g5p</guid>
      <description>&lt;p&gt;"Buy the dip" is the most repeated advice in crypto — and following it blindly is one of the most reliable ways to lose money. A 6% pullback inside a bull market and a 6% bleed on the way to zero print the same red candle. One is an opportunity; the other is a knife.&lt;/p&gt;

&lt;p&gt;We built &lt;strong&gt;Trend Dip&lt;/strong&gt;, a pullback strategy that only buys reclaims inside an uptrend, validated it walk-forward across 40 coins on 4-hour bars, and shipped it as a free preset in our in-browser backtester — no signup, the whole engine is WASM running locally in your browser, so you can re-run every number in this post on any coin in about a second.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule (two lines)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ENTRY:CROSS(C,LOWER) &amp;amp;&amp;amp; C&amp;gt;TRENDMA    // buy the reclaim of the lower Bollinger band, in an uptrend
EXIT:CROSS(C,MID)                    // sell at the mid band
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…where &lt;code&gt;LOWER = MA(C,20) − 2·STD(C,20)&lt;/code&gt;, &lt;code&gt;MID = MA(C,20)&lt;/code&gt;, and &lt;code&gt;TRENDMA = MA(C,100)&lt;/code&gt;. Each piece earns its place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C &amp;gt; MA100 — the trend filter.&lt;/strong&gt; This is what separates a dip from a knife. Below the 100-bar average, the same band reclaim is ignored entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CROSS(C, LOWER) — buy the reclaim, not the touch.&lt;/strong&gt; The entry fires on the bar whose &lt;em&gt;close comes back above&lt;/em&gt; the band. The market has already rejected the low once before you spend a cent. This single change moved the OOS win rate by roughly ten points versus buying the first touch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit at the mid band.&lt;/strong&gt; The thesis is snap-back to the 20-bar mean, so that's where the trade ends. We tried smarter exits — trailing lows, trend-break exits — every one performed &lt;em&gt;worse&lt;/em&gt; out of sample.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No stop in the formula.&lt;/strong&gt; Deliberate: 4h crypto charts routinely flush 5% below the band and revert by the next close, and a stop converts those wicks into realized losses. Adding a 10–20% protective stop changed the results almost imperceptibly (stops almost never fired). The backtester still gives you an optional stop field if you want a hard tail cap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo7wmlxt3wwpbfzwhxzdr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo7wmlxt3wwpbfzwhxzdr.webp" alt="Trend Dip backtest page on BTC/USDT 4h — chart, formula, equity curve, stats grid and trade list" width="800" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Four rules, all of which matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Not one chart — forty.&lt;/strong&gt; BTC, ETH, SOL, BNB, XRP, DOGE, AVAX, DOT and 32 more majors with long histories. A rule that only works on a cherry-picked chart is a screenshot, not an edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Walk-forward, not full-history.&lt;/strong&gt; Each coin's ~500 days of 4h bars split 70/30: the first 70% is where parameters would effectively have been chosen; the final 30% is scored blind as one continuous out-of-sample segment. All headline numbers below are OOS only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Realistic fills.&lt;/strong&gt; Signals confirm at bar close, fill at the &lt;em&gt;next&lt;/em&gt; bar's open — no lookahead — with 0.10% taker fee + 0.05% slippage per side (~0.3% round trip). The same engine and simulator that power the public backtest page ran every test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmarks.&lt;/strong&gt; The identical harness scored the nine other strategies on the site on the same data.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Out-of-sample metric (40 coins, 4h, 70/30)&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Win rate, all held-out trades&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;73.6%&lt;/strong&gt; (174 trades)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Win rate, in-sample (for contrast)&lt;/td&gt;
&lt;td&gt;62.9% (267 trades)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coins profitable OOS&lt;/td&gt;
&lt;td&gt;26 / 40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coins beating buy &amp;amp; hold OOS&lt;/td&gt;
&lt;td&gt;24 / 40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coins winning ≥50% of held-out trades&lt;/td&gt;
&lt;td&gt;33 / 35 with ≥3 trades&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median per-coin max drawdown&lt;/td&gt;
&lt;td&gt;−6.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worst / median / best held-out trade&lt;/td&gt;
&lt;td&gt;−16.7% / +1.2% / +34.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average holding time&lt;/td&gt;
&lt;td&gt;12 bars ≈ 2 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Win rate at 2× fees&lt;/td&gt;
&lt;td&gt;69.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BTC / ETH / SOL win rate&lt;/td&gt;
&lt;td&gt;83% / 75% / 100%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two details deserve a second look. First, &lt;strong&gt;the OOS win rate is &lt;em&gt;higher&lt;/em&gt; than the in-sample one&lt;/strong&gt; — overfit strategies show the opposite signature: brilliant in the tuning window, collapsing the moment data is unfamiliar. Second, the edge survives fees at two to three times what a normal spot account pays.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1fqexrt4niqr82csua4.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1fqexrt4niqr82csua4.webp" alt="Statistics grid and the full trade list for the BTC run" width="799" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying hard to break it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Six consecutive time slices&lt;/strong&gt; scored separately: win rates 71%, 56%, 62%, 55%, 69%, 75% — every window above 50%. A regime-dependent fluke does not do that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter plateau.&lt;/strong&gt; Defaults (20 bars, 2σ, MA100) sit in the middle of a plateau: 1.8σ–2.5σ all won 70–77%, neighbouring trend filters (50–200) all stayed positive. If performance collapses when you nudge a parameter 10%, you found noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split-date sensitivity.&lt;/strong&gt; Moving the walk-forward cut across 50/30 → 90/10 changed the aggregate win rate only between 55% and 66%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit shoot-outs.&lt;/strong&gt; Adding a 10-bar-low emergency exit or a trend-break exit &lt;em&gt;lowered&lt;/em&gt; the OOS win rate (61.5% and 51.0%). The boring mid-band exit won — stops were selling into the very wicks the strategy profits from reverting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmarks:&lt;/strong&gt; the best of the nine existing strategies managed a 51% OOS win rate on the same data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where it loses — read this part
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;73.6% win rate is not a money printer.&lt;/strong&gt; The median winner is small (+1.2%) because the target — the mid band — is close. The profile is many small wins, occasional runners, occasional give-backs. The worst held-out trade lost 16.7%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No formula stop means tail exposure exists.&lt;/strong&gt; Historically the MA100 filter and mid-band exit capped the damage, but a coin that breaks down mid-trade holds until it recovers to the mid band. If that worries you, set the optional stop in the backtester and accept slightly worse expected numbers for a hard floor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The held-out window is one market regime&lt;/strong&gt; (~5 months per coin, dips inside an uptrend). In a full bear market the MA100 filter will simply keep the strategy flat for long stretches — designed behavior, not a bonus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Survivorship bias is real&lt;/strong&gt; — today's top-40 list excludes the coins that died.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Run it across the whole market
&lt;/h2&gt;

&lt;p&gt;One coin's backtest is an anecdote. The &lt;a href="https://chartcrypto.app/scanner/trend-dip" rel="noopener noreferrer"&gt;scanner page&lt;/a&gt; runs the identical formula across a whole universe in one pass — each coin split walk-forward, ranked by out-of-sample Sharpe:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftlzou59c1p0lvj4dmi41.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftlzou59c1p0lvj4dmi41.webp" alt="Multi-coin scanner running Trend Dip across the top-20 universe" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the hood (the dev part)
&lt;/h2&gt;

&lt;p&gt;The whole thing — formula parser, indicator engine, backtest simulator, walk-forward scanner — is a Rust→WASM module evaluated entirely in your browser on real exchange candles. The strategy DSL is a tiny TDX-style language (&lt;code&gt;ENTRY:CROSS(C,LOWER) &amp;amp;&amp;amp; C&amp;gt;TRENDMA;&lt;/code&gt;), so every rule on the site is user-editable text, and the simulator fills at the next bar's open with configurable fees and slippage. Nothing you type is uploaded anywhere.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Re-run this exact strategy on any pair: &lt;a href="https://chartcrypto.app/backtesting/trend-dip" rel="noopener noreferrer"&gt;backtest page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Scan the whole universe: &lt;a href="https://chartcrypto.app/scanner/trend-dip" rel="noopener noreferrer"&gt;scanner page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full write-up with more stress tests: &lt;a href="https://chartcrypto.app/blog/trend-dip-indicator-design" rel="noopener noreferrer"&gt;original article&lt;/a&gt; &lt;em&gt;(canonical URL of this post — it's the same article, cross-posted)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; everything above is a historical backtest, not a promise. Backtested win rates decay in live trading. Not financial advice — crypto is volatile and you can lose your entire investment.&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>trading</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Testing automata-theory exercises in the browser: a regex flavor for CS coursework</title>
      <dc:creator>FF</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:19:22 +0000</pubDate>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3/testing-automata-theory-exercises-in-the-browser-a-regex-flavor-for-cs-coursework-3kio</link>
      <guid>https://dev.to/fu_liang_dfbec863ed6c21e3/testing-automata-theory-exercises-in-the-browser-a-regex-flavor-for-cs-coursework-3kio</guid>
      <description>&lt;p&gt;If you've ever taught or taken a formal languages course, you know the workflow: the exercise says &lt;em&gt;write a regular expression for all strings over {a,b} containing an even number of a's&lt;/em&gt;, you write &lt;code&gt;(b|ab)*&lt;/code&gt; on paper... and then you have no way to check it.&lt;/p&gt;

&lt;p&gt;Programming regex flavors can't validate it for you — they're full of conveniences the formal dialect doesn't have: character classes, quantifier shorthands, anchors. What you want is a checker that speaks the &lt;strong&gt;textbook dialect&lt;/strong&gt;: union &lt;code&gt;∪&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt;, concatenation, Kleene star &lt;code&gt;*&lt;/code&gt;, plus &lt;code&gt;+&lt;/code&gt;, parentheses. Nothing more.&lt;/p&gt;

&lt;p&gt;RegexTester.dev now ships a &lt;strong&gt;Formal Automata Theory flavor&lt;/strong&gt; exactly for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it accepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Symbols from your chosen alphabet (typically &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, or &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Union: &lt;code&gt;∪&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Concatenation: juxtaposition or &lt;code&gt;.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Kleene star: &lt;code&gt;*&lt;/code&gt;, and one-or-more: &lt;code&gt;+&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Grouping with parentheses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything else — &lt;code&gt;\d&lt;/code&gt;, &lt;code&gt;[a-z]&lt;/code&gt;, lookahead, backreferences — is rejected with an explanation, because the point is to exercise exactly the constructs the course covers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instant feedback
&lt;/h2&gt;

&lt;p&gt;Type the expression and test strings in, and you immediately see which strings match and which don't — so you can iterate on the exercise instead of waiting for a grader.&lt;/p&gt;

&lt;p&gt;The flavor runs on the same engine infrastructure as the other eight dialects (PCRE2, RE2, .NET, Python, Java, Go, Rust, JavaScript), so the matching behavior is well-defined and consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a browser tool for coursework?
&lt;/h2&gt;

&lt;p&gt;Because the feedback loop is everything when learning. The gap between writing &lt;code&gt;(b|ab)*&lt;/code&gt; on paper and knowing whether it's right is where most of the confusion in a formal languages course lives. Close that gap to seconds and students experiment their way to understanding: they can &lt;em&gt;see&lt;/em&gt; that &lt;code&gt;(b|ab)*&lt;/code&gt; matches &lt;code&gt;babb&lt;/code&gt; but not &lt;code&gt;abab&lt;/code&gt;, and fix their mental model immediately.&lt;/p&gt;

&lt;p&gt;It also works the other direction: instructors can build exercise sets with permalinks — every regex and test string combination in the tester has a shareable URL, so "check your answer against this" becomes a link.&lt;/p&gt;




&lt;p&gt;Try it: open &lt;a href="https://regextester.dev" rel="noopener noreferrer"&gt;regextester.dev&lt;/a&gt;, pick the &lt;strong&gt;Formal Automata&lt;/strong&gt; flavor from the selector, and test that even-number-of-a's exercise.&lt;/p&gt;

&lt;p&gt;Full write-up: &lt;a href="https://regextester.dev/blog/Formal-automata-theory-flavor" rel="noopener noreferrer"&gt;regextester.dev/blog/Formal-automata-theory-flavor&lt;/a&gt;. Free, no signup — built for students and educators.&lt;/p&gt;

</description>
      <category>regex</category>
      <category>computerscience</category>
      <category>education</category>
      <category>theory</category>
    </item>
    <item>
      <title>Benchmarking regex patterns properly: P95/P99, equivalence checks, real engines</title>
      <dc:creator>FF</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:08:30 +0000</pubDate>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3/benchmarking-regex-patterns-properly-p95p99-equivalence-checks-real-engines-1cij</link>
      <guid>https://dev.to/fu_liang_dfbec863ed6c21e3/benchmarking-regex-patterns-properly-p95p99-equivalence-checks-real-engines-1cij</guid>
      <description>&lt;p&gt;Every developer has been here: two regex patterns that both work. One is a dense one-liner, the other reads like prose. Which do you ship?&lt;/p&gt;

&lt;p&gt;"Use the simpler one" is a fine heuristic until the pattern runs over a 50 MB log file or every request in a hot path. Then you want data. The problem is that regex benchmarks done casually lie to you three ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The engine problem&lt;/strong&gt; — your JS benchmark measures JavaScript's RegExp, but the pattern ships to a PCRE2-based backend. Different engine, different behavior, different speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The clock problem&lt;/strong&gt; — a fast regex finishes in microseconds, far below &lt;code&gt;performance.now()&lt;/code&gt; resolution. You're measuring timer noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The equivalence problem&lt;/strong&gt; — the faster pattern is faster &lt;em&gt;because it matches less&lt;/em&gt;. You optimized a bug.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;RegexTester.dev's built-in &lt;a href="https://regextester.dev/bench" rel="noopener noreferrer"&gt;Benchmark Suite&lt;/a&gt; was designed against those three traps. Here's how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real engines, identical conditions
&lt;/h2&gt;

&lt;p&gt;Up to 5 alternative patterns run sequentially under identical input, flags and engine — the same engines the tester itself uses: PCRE2 and RE2 as WebAssembly in a dedicated worker, .NET's &lt;code&gt;System.Text.RegularExpressions&lt;/code&gt; as a real interpreter, and native JS RegExp (including the normalize-to-JS path used for Python, Java and other flavors).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqqf6buhrj9rgna7uzxn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqqf6buhrj9rgna7uzxn.webp" alt="Benchmark setup" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sub-clock-precision timing
&lt;/h2&gt;

&lt;p&gt;Each sample times &lt;strong&gt;K consecutive executions&lt;/strong&gt; and records time ÷ K, with K auto-calibrated so every sample spans between 0.35 ms and 12 ms. This batch amplification resolves sub-microsecond patterns with sub-nanosecond effective resolution — far below browser clock precision.&lt;/p&gt;

&lt;p&gt;You get median, mean, P95, P99, ops/sec and the relative speedup of the fastest pattern, with a ±RSE column that tells you when a number is too noisy to trust.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtubrg6maeddkcplsjkd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtubrg6maeddkcplsjkd.webp" alt="Results table" width="800" height="945"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Equivalence checking
&lt;/h2&gt;

&lt;p&gt;Speed means nothing if the output changed. The suite records each pattern's match count on the benchmark input and raises an &lt;strong&gt;"Output differs"&lt;/strong&gt; warning when counts diverge — a pattern that's faster because it matches less gets flagged before you ship it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the charts
&lt;/h2&gt;

&lt;p&gt;Latency histograms and timelines make the distribution shape visible: a tight spike means stable performance; a long tail means some inputs hit pathological backtracking even when the median looks fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0j0c8rbmtcseze59lwem.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0j0c8rbmtcseze59lwem.webp" alt="Charts" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Export
&lt;/h2&gt;

&lt;p&gt;Results export as Markdown (for PRs and docs) or JSON (for your own tooling), with the environment (UA, cores, timestamp) baked in so numbers stay comparable.&lt;/p&gt;




&lt;p&gt;Try it: open &lt;a href="https://regextester.dev/bench" rel="noopener noreferrer"&gt;regextester.dev/bench&lt;/a&gt;, paste two alternatives, press Run. Sixty seconds later you'll know which one to ship — and whether they really do the same thing.&lt;/p&gt;

&lt;p&gt;Full write-up: &lt;a href="https://regextester.dev/blog/regex-benchmark-suite" rel="noopener noreferrer"&gt;regextester.dev/blog/regex-benchmark-suite&lt;/a&gt;. Free, no signup, everything runs locally.&lt;/p&gt;

</description>
      <category>regex</category>
      <category>benchmarking</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built a regex debugger that shows every backtrack (flame graph included)</title>
      <dc:creator>FF</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:08:29 +0000</pubDate>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3/i-built-a-regex-debugger-that-shows-every-backtrack-flame-graph-included-g5e</link>
      <guid>https://dev.to/fu_liang_dfbec863ed6c21e3/i-built-a-regex-debugger-that-shows-every-backtrack-flame-graph-included-g5e</guid>
      <description>&lt;p&gt;Testers tell you &lt;em&gt;what&lt;/em&gt; a regex matches. A debugger tells you &lt;em&gt;why&lt;/em&gt; — and &lt;em&gt;where it burns time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Every regex tester shows the same three things: matches, groups, and an explanation of the pattern. That covers the happy path. It stops helping the moment a regex is &lt;em&gt;almost&lt;/em&gt; right — matching one tag too many, missing a case you can't reproduce, or taking 400 ms on an input that looks identical to the one that took 4.&lt;/p&gt;

&lt;p&gt;The failure modes of a backtracking engine are invisible from the outside. A pattern doesn't slowly get slower — it tries a prefix, gives characters back, retries the inner quantifier at a different split, fails, and repeats that a few thousand times. The match result looks the same as the fast version's. Only the &lt;strong&gt;trace&lt;/strong&gt; of what the engine actually did tells them apart.&lt;/p&gt;

&lt;p&gt;So I built a full debugger into &lt;a href="https://regextester.dev" rel="noopener noreferrer"&gt;RegexTester.dev&lt;/a&gt;. Here's what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  One click from the tester
&lt;/h2&gt;

&lt;p&gt;The entry point is the &lt;strong&gt;Debug&lt;/strong&gt; button on the tester toolbar. Hit it, and the debugger opens with the trace already recorded. The demo pattern &lt;code&gt;&amp;lt;([a-z]+)([^&amp;lt;]+)*(?:&amp;gt;(.*)&amp;lt;\/\1&amp;gt;|\s+/&amp;gt;)&lt;/code&gt; against &lt;code&gt;&amp;lt;div&amp;gt;Content&amp;lt;/div&amp;gt;&lt;/code&gt; generates &lt;strong&gt;7,530 execution steps&lt;/strong&gt; for 18 characters of input. That gap between "looks simple" and "7,530 steps" is exactly what the debugger makes visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg3jdbik9gqut0a891sl.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg3jdbik9gqut0a891sl.webp" alt="Regex debugger overview" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The execution graph
&lt;/h2&gt;

&lt;p&gt;The graph is a flame-chart of the engine's work. Each bar is one node attempt — a TRY that ended in OK (green), FAIL (red), or was undone by backtracking (thin blue give-back marks). Rows are stack depth; vertical bands separate match attempts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4q1ajn78wzzd16rxq2ai.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4q1ajn78wzzd16rxq2ai.webp" alt="Execution graph" width="797" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It behaves like a proper profiler view: scroll-wheel zoom, drag to pan, double-click to zoom to a block, click any block to jump there — the amber marker follows, and every other panel updates in lockstep. A healthy trace reads as a calm staircase. A sick one shows the signature immediately: one region of the graph orders of magnitude wider than everything else, striped with red and blue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hot spots: where the steps go
&lt;/h2&gt;

&lt;p&gt;The Hot spots table is the profiler summary: every node of your pattern, ranked, with its operation count, give-backs, failures, and share of total steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshmofmilggsj5bfenba5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshmofmilggsj5bfenba5.webp" alt="Hot spots table" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the same workflow you'd use on slow code: profile, find the hot function, fix, re-profile. It just didn't exist for regular expressions in a browser before.&lt;/p&gt;

&lt;h2&gt;
  
  
  From diagnosis to fix in two clicks
&lt;/h2&gt;

&lt;p&gt;Diagnosis without a fix is only half a tool. The &lt;strong&gt;Suggestions&lt;/strong&gt; panel combines static analysis with trace statistics to propose concrete rewrites — dialect-aware, so a PCRE2 pattern gets atomic groups and possessive quantifiers while a JavaScript pattern gets the constructs ES2025 actually supports.&lt;/p&gt;

&lt;p&gt;The classic case: &lt;code&gt;(a+)+b&lt;/code&gt; on &lt;code&gt;aaaaaaaaaaX&lt;/code&gt; — the textbook catastrophic backtracking example. The debugger names the disease and prescribes the cure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgqilqkba4s8qu7zxlle.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgqilqkba4s8qu7zxlle.webp" alt="Suggestions panel" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two things make this safe to trust:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Equivalence checking&lt;/strong&gt; — both patterns run against the real engine for your dialect on your input; only identical match lists earn the "same matches on this input" badge. A faster regex that matches less is a bug, not an optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply&lt;/strong&gt; writes the rewrite into the tester and re-traces immediately: same input, Healthy verdict, 404 steps instead of 50,000+.&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/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgcr6bx5h1q95zdk2wvn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgcr6bx5h1q95zdk2wvn.webp" alt="After applying the rewrite" width="797" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A tracer kept honest
&lt;/h2&gt;

&lt;p&gt;The trace comes from an instrumented backtracking engine — a continuation-passing matcher written for this purpose, speaking the semantics of nine flavor dialects. Every trace is &lt;strong&gt;cross-checked against the real thing&lt;/strong&gt;: the match outcome is compared with the actual engine for the selected flavor (native RegExp, or PCRE2/.NET compiled to WebAssembly), and a "trace verified" badge only appears when they agree.&lt;/p&gt;

&lt;p&gt;Try it: &lt;a href="https://regextester.dev/debug" rel="noopener noreferrer"&gt;regextester.dev/debug&lt;/a&gt; — or read the full write-up at &lt;a href="https://regextester.dev/blog/regex-debugger" rel="noopener noreferrer"&gt;regextester.dev/blog/regex-debugger&lt;/a&gt;. Free, no signup, everything runs locally.&lt;/p&gt;

</description>
      <category>regex</category>
      <category>debugging</category>
      <category>javascript</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
