<?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: Efficienco</title>
    <description>The latest articles on DEV Community by Efficienco (@efficienco).</description>
    <link>https://dev.to/efficienco</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%2F4097051%2F6dcfc98f-f8e9-49b9-b1a7-e81c0bdfd836.png</url>
      <title>DEV Community: Efficienco</title>
      <link>https://dev.to/efficienco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/efficienco"/>
    <language>en</language>
    <item>
      <title>What Building 77 Browser-Based Calculators Taught Me About Input Validation</title>
      <dc:creator>Efficienco</dc:creator>
      <pubDate>Sun, 30 Aug 2026 05:45:41 +0000</pubDate>
      <link>https://dev.to/efficienco/what-building-77-browser-based-calculators-taught-me-about-input-validation-mf2</link>
      <guid>https://dev.to/efficienco/what-building-77-browser-based-calculators-taught-me-about-input-validation-mf2</guid>
      <description>&lt;p&gt;Building one calculator is straightforward. Building dozens of calculators with different units, assumptions, ranges, and failure modes is where input handling becomes the real product.&lt;/p&gt;

&lt;p&gt;While building 77 free browser-based calculators for &lt;a href="https://efficienco.com/" rel="noopener noreferrer"&gt;Efficienco&lt;/a&gt;, I found that the formula was rarely the part that caused the most trouble. The difficult part was deciding what every input actually meant and what should happen when someone entered something unexpected.&lt;/p&gt;

&lt;p&gt;This article covers the validation patterns that became useful across the project. It does not cover the calculation formulas themselves. The focus is everything that must happen before and after a formula runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Empty, zero, and invalid are different states
&lt;/h2&gt;

&lt;p&gt;A common mistake is treating every falsy value as missing:&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter a value&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rejects &lt;code&gt;0&lt;/code&gt;, even when zero is a valid input. It also fails to explain whether the user left the field empty or entered something the calculator could not parse.&lt;/p&gt;

&lt;p&gt;I started handling those states separately:&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;readFiniteNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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;raw&lt;/span&gt; &lt;span class="o"&gt;===&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;empty&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&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;raw&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="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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 calculator can then give a useful response instead of one generic error for everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Validate the domain, not only the data type
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;12&lt;/code&gt; is a valid number, but it may still be invalid for a particular field.&lt;/p&gt;

&lt;p&gt;A percentage may need to stay between 0 and 100. A wall length cannot be negative. A pipe diameter of zero makes no physical sense. A financial time period may need to be a whole number.&lt;/p&gt;

&lt;p&gt;Type validation answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this a number?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Domain validation answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this number meaningful here?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I found it useful to keep those checks explicit:&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;requireRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;min&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;max&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; must be between &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;max&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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 ranges should reflect the field's meaning rather than being copied across every calculator.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Normalize units at the boundary
&lt;/h2&gt;

&lt;p&gt;When a calculator supports feet, metres, inches, millimetres, gallons, litres, kilograms, and pounds, it becomes tempting to perform conversions throughout the formula.&lt;/p&gt;

&lt;p&gt;That quickly becomes difficult to audit.&lt;/p&gt;

&lt;p&gt;The cleaner pattern was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the user's value.&lt;/li&gt;
&lt;li&gt;Convert it into one internal base unit.&lt;/li&gt;
&lt;li&gt;Run the calculation using only base units.&lt;/li&gt;
&lt;li&gt;Convert the final result into the requested display unit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This creates a clear boundary between input formatting and the underlying calculation. It also makes unit-switching tests much easier: equivalent measurements should produce equivalent results.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Never round intermediate values
&lt;/h2&gt;

&lt;p&gt;Rounding early can create surprisingly large differences when several stages are involved.&lt;/p&gt;

&lt;p&gt;For example, a material estimate might calculate an area, apply a coverage factor, add waste, and finally round up to purchasable units. Rounding after every stage compounds the error.&lt;/p&gt;

&lt;p&gt;The safer rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep full precision internally and round only for display or purchasing requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Display rounding and operational rounding are also different. Showing &lt;code&gt;12.47&lt;/code&gt; to a user is a formatting choice. Rounding a purchase requirement up to &lt;code&gt;13&lt;/code&gt; packages is part of the result's meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Assumptions belong beside the input
&lt;/h2&gt;

&lt;p&gt;Many calculators cannot produce a useful result without assumptions. Material waste, labour rates, compaction, occupancy, efficiency, and safety factors are examples.&lt;/p&gt;

&lt;p&gt;Hiding those assumptions inside the JavaScript makes the result look more precise than it really is.&lt;/p&gt;

&lt;p&gt;I found three things helped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provide a sensible default.&lt;/li&gt;
&lt;li&gt;Let the user change it when practical.&lt;/li&gt;
&lt;li&gt;Explain what the default represents beside the field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to show every implementation detail. It is to ensure users understand which inputs materially affect the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Error messages should explain the repair
&lt;/h2&gt;

&lt;p&gt;“Invalid input” describes the software's problem, not the user's next action.&lt;/p&gt;

&lt;p&gt;More useful messages are specific:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Wall length must be greater than zero.”&lt;/li&gt;
&lt;li&gt;“Waste percentage must be between 0% and 100%.”&lt;/li&gt;
&lt;li&gt;“Enter a valid pipe diameter.”&lt;/li&gt;
&lt;li&gt;“The minimum value cannot exceed the maximum value.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best validation messages tell users exactly what to change without making them understand the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Test relationships, not just individual values
&lt;/h2&gt;

&lt;p&gt;Some inputs are valid on their own but invalid in combination.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A minimum value greater than the maximum.&lt;/li&gt;
&lt;li&gt;A start date later than the end date.&lt;/li&gt;
&lt;li&gt;A wall opening larger than the wall.&lt;/li&gt;
&lt;li&gt;A down payment greater than the purchase price.&lt;/li&gt;
&lt;li&gt;A result unit that does not match the selected measurement system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These relational checks are easy to miss when every field is tested independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The biggest lesson
&lt;/h2&gt;

&lt;p&gt;Users judge a calculator by whether the answer feels dependable, not by how elegant its formula looks in the source code.&lt;/p&gt;

&lt;p&gt;That dependability comes from correctly handling messy input, explaining assumptions, preserving precision, and producing useful errors.&lt;/p&gt;

&lt;p&gt;One practical example is the &lt;a href="https://efficienco.com/tools/materials/concrete-block-calculator/" rel="noopener noreferrer"&gt;Concrete Block Calculator&lt;/a&gt;, where dimensions, openings, spacing assumptions, waste, and purchasing quantities all need clearly separated treatment. The public tool demonstrates the experience, while the implementation remains private.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Calculate Bitumen Quantity for Roads and Driveways (With Formula)</title>
      <dc:creator>Efficienco</dc:creator>
      <pubDate>Thu, 27 Aug 2026 09:31:45 +0000</pubDate>
      <link>https://dev.to/efficienco/how-to-calculate-bitumen-quantity-for-roads-and-driveways-with-formula-38jb</link>
      <guid>https://dev.to/efficienco/how-to-calculate-bitumen-quantity-for-roads-and-driveways-with-formula-38jb</guid>
      <description>&lt;p&gt;Getting bitumen quantity wrong on a paving job is expensive in both directions — order too little and you're waiting on a second delivery mid-pour, order too much and you're eating the cost of unused material. Here's the actual math contractors and estimators use, so you can sanity-check any calculator (including automated ones) before you commit to an order.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The core formula&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Bitumen requirement for a road or driveway surface comes down to three inputs:&lt;/p&gt;

&lt;p&gt;Bitumen quantity (kg) = Area (m²) × Bitumen rate (kg/m²) × Number of coats&lt;br&gt;
Area — length × width of the surface you're covering, in square meters.&lt;br&gt;
Bitumen rate — the application rate, typically expressed in kg per m². This varies by surface type and coat purpose (tack coat, prime coat, or wearing course all have different standard rates).&lt;br&gt;
Coats — most jobs use a prime coat plus a tack coat, and sometimes a separate seal coat — each with its own rate, so don't assume "one number fits all layers."&lt;br&gt;
Typical application rates&lt;/p&gt;

&lt;p&gt;As a rough reference (always confirm against your local spec or the binder manufacturer's datasheet, since rates vary by climate and surface type):&lt;/p&gt;

&lt;p&gt;Prime coat: ~0.6–1.0 kg/m²&lt;br&gt;
Tack coat: ~0.2–0.5 kg/m²&lt;br&gt;
Surface dressing / wearing course: ~1.0–2.5 kg/m², depending on aggregate size&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;A worked example&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Say you're prime-coating a 500 m² driveway at 0.8 kg/m²:&lt;/p&gt;

&lt;p&gt;500 m² × 0.8 kg/m² × 1 coat = 400 kg of bitumen&lt;/p&gt;

&lt;p&gt;Add a tack coat at 0.3 kg/m² on the same area and you need another 150 kg — 550 kg total before you account for wastage.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Don't forget wastage&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Add 5–10% on top of the calculated figure for spillage, uneven application, and edge overruns. Estimators who skip this step are usually the ones making the emergency second order.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Why I built a calculator for this anyway&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
The formula above is simple, but juggling multiple coats, unit conversions, and wastage percentages by hand on-site is where errors creep in. I built a free &lt;a href="https://efficienco.com/tools/materials/bitumen-calculator/" rel="noopener noreferrer"&gt;Bitumen Calculator&lt;/a&gt; that takes area, rate, and coat count and gives you the total instantly — useful for a quick double-check even if you're running your own spreadsheet for the official estimate.&lt;/p&gt;

</description>
      <category>construction</category>
      <category>engineering</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
