<?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: Seiya Izumi</title>
    <description>The latest articles on DEV Community by Seiya Izumi (@izumisy).</description>
    <link>https://dev.to/izumisy</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%2F41703%2F84fea402-3285-487f-825a-2a2b2a475a7c.jpg</url>
      <title>DEV Community: Seiya Izumi</title>
      <link>https://dev.to/izumisy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/izumisy"/>
    <language>en</language>
    <item>
      <title>Curing a Transcription Worker That Kept Dying on Cloud Run with Streaming</title>
      <dc:creator>Seiya Izumi</dc:creator>
      <pubDate>Tue, 16 Jun 2026 15:08:10 +0000</pubDate>
      <link>https://dev.to/izumisy/curing-a-transcription-worker-that-kept-dying-on-cloud-run-with-streaming-bpm</link>
      <guid>https://dev.to/izumisy/curing-a-transcription-worker-that-kept-dying-on-cloud-run-with-streaming-bpm</guid>
      <description>&lt;p&gt;An audio transcription worker kept getting killed by OOM on Cloud Run. &lt;/p&gt;

&lt;p&gt;Bumping the memory allocation calmed it down for a while, but that only inflated the bill without fixing anything. Crash, raise the limit, crash again — that was the steady state.&lt;/p&gt;

&lt;p&gt;In the end, changing &lt;em&gt;how&lt;/em&gt; the processing was structured improved memory, cost, transcription accuracy, and testability all at once. This post is a record of the path I took from treating symptoms to curing the root cause.&lt;/p&gt;

&lt;p&gt;The punchline up front: what actually worked wasn't a clever algorithm. It was simply &lt;strong&gt;dropping "load everything into memory, then process" in favor of a streaming design — "read and forward as you go."&lt;/strong&gt; That's it. But getting there required clearly articulating &lt;em&gt;why&lt;/em&gt; it was dying, and identifying where the existing setup was carrying a structural impossibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the System Did
&lt;/h2&gt;

&lt;p&gt;It transcribed the audio from user-uploaded mp4 files by sending it to a third-party transcription Web API. The slightly awkward part: that API &lt;strong&gt;only accepts wav&lt;/strong&gt;. Uploads come in as mp4, the API wants wav. The conversion had to happen somewhere.&lt;/p&gt;

&lt;p&gt;The runtime was GCP Cloud Run, the language was Go. The architecture was an asynchronous job: an upload enqueued a task to Cloud Tasks, which dispatched a worker that ran the transcription.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup I Inherited, and Its Limits
&lt;/h2&gt;

&lt;p&gt;By the time I took it over, the inside of the worker worked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch ffmpeg from the application process&lt;/li&gt;
&lt;li&gt;Split the mp4 into 15-second wav files&lt;/li&gt;
&lt;li&gt;Load each wav into memory and send it to the transcription API&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It worked. But the worker was dying to OOM constantly.&lt;/p&gt;

&lt;p&gt;Why? Breaking it down calmly, two memory-hungry factors were stacked on top of each other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ffmpeg ran as a separate process&lt;/strong&gt;, carrying its own resident memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;wav is uncompressed&lt;/strong&gt;, so it sat in memory far heavier than the equivalent mp4, and the design held that data in memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And Cloud Run handles multiple tasks concurrently within a single container. So when several transcription tasks piled into the same container, this heavy processing multiplied accordingly. The moment &lt;em&gt;single-task peak memory × concurrent tasks&lt;/em&gt; exceeded the container's memory ceiling, the whole container went down to OOM. A single large mp4 could push it over on its own, and overlapping tasks made it worse. In short, it was a design &lt;strong&gt;guaranteed to fall over eventually&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Number "15" Was Telling Me
&lt;/h3&gt;

&lt;p&gt;What snagged my attention while reading the code was &lt;em&gt;why&lt;/em&gt; the split unit was 15 seconds.&lt;/p&gt;

&lt;p&gt;Digging in, it turned out to be a compromise the previous engineer had found within the memory constraints. Make the chunks longer and each wav gets heavier, pressuring memory and increasing OOMs. Make them shorter and memory eases up, but now utterances get chopped at chunk boundaries, hurting transcription accuracy — and the request count goes up too.&lt;/p&gt;

&lt;p&gt;15 seconds was the equilibrium point of that tug-of-war.&lt;/p&gt;

&lt;p&gt;And here's what struck me: &lt;strong&gt;the number 15 wasn't a solution to the problem — it was a constraint born out of being shackled to the memory limit.&lt;/strong&gt; Memory and accuracy, two genuinely separate concerns, were pushing against each other on a single parameter: the split length. Raise one and the other falls. The previous engineer had been carefully balancing on top of that, but if the tradeoff didn't exist in the first place, nobody would have to agonize over it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Another Parameter, Set Carelessly
&lt;/h3&gt;

&lt;p&gt;The same structure showed up in the concurrency setting. The worker's concurrency had no sign of being deliberately tuned — it was set roughly. My guess is it came from a cost/ops instinct: "I don't want to spin up a lot of instances."&lt;/p&gt;

&lt;p&gt;But this meshed in the worst possible way. To save on instances, you set concurrency high. Multiple tasks then pile into one container, the heavy memory processing multiplies, and OOM kills the container.&lt;/p&gt;

&lt;p&gt;The setting meant to reduce instances was, instead, killing the containers. Raise memory and the cost climbs; concurrency tugs against instance count; split length wavers between memory and accuracy. Small tradeoffs tangled everywhere, and the whole thing was stuck at an equilibrium nobody was happy with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Let the Diagnosis Stay a Guess
&lt;/h2&gt;

&lt;p&gt;Before touching anything, I first confirmed that memory really was the cause. Deciding the reason by hunch wastes time on misdirected fixes.&lt;/p&gt;

&lt;p&gt;I used Cloud Trace to repeatedly follow which segments of a request inflated resources, and how that correlated with container OOM terminations. That narrowed it down to the wav memory loading and the ffmpeg process. Once the source is pinned down, the direction of the fix follows naturally — reduce what gets loaded into memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shift in Thinking: Stream While Reading, Not Load Then Forward
&lt;/h2&gt;

&lt;p&gt;The essential waste in the old setup was right here: expanding the entire file (or the entire split wav) into memory before sending it to the API. But looking closely, the transcription API actually supported streaming input.&lt;/p&gt;

&lt;p&gt;So this became possible: &lt;strong&gt;read the mp4 from storage a little at a time, convert it into audio chunks on the fly, and pipe it straight to the API.&lt;/strong&gt; There's no need to pool the whole thing in memory anywhere. Only the small chunk currently being processed sits in memory, and what has finished streaming is released.&lt;/p&gt;

&lt;p&gt;Concretely: read the mp4 from storage incrementally with a section reader, parse the container with a stable third-party demuxer to extract audio chunks, decode those with fdk-aac, and pipe the result to the transcription API as a stream.&lt;/p&gt;

&lt;p&gt;The crux of this design is that &lt;strong&gt;peak memory stops depending on file size&lt;/strong&gt;. Ten minutes of audio or an hour, what sits in memory at once is only the chunk being processed. The peak becomes roughly constant.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do with ffmpeg — Isolation, Not Elimination
&lt;/h2&gt;

&lt;p&gt;Here I had a call to make. The old setup used ffmpeg for the mp4 → wav conversion. What to do with it in the new one?&lt;/p&gt;

&lt;p&gt;I wanted to avoid launching ffmpeg as a separate process on every transcription. The reasons stacked up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spawning a process per task means processes proliferate with concurrency, and resident memory piles up&lt;/li&gt;
&lt;li&gt;Process startup/teardown has latency, and handing data over stdin/stdout copies it across the process boundary&lt;/li&gt;
&lt;li&gt;Above all, &lt;strong&gt;an external process's memory usage can't be controlled or observed from the application side&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if streaming keeps the application's peak memory down, if I can't hold the reins on what an external process consumes, I can't &lt;em&gt;guarantee&lt;/em&gt; memory by design. On Cloud Run, where the memory allocation maps directly onto the bill, being able to guarantee the peak ties directly to severing the cost-vs-stability tradeoff.&lt;/p&gt;

&lt;p&gt;That said, I couldn't eliminate ffmpeg entirely. The mp4 files users upload vary in codec and container layout. The demuxer and fdk-aac can cleanly handle only known, standard formats. Push arbitrary input straight into the hot path and it breaks easily on edge cases.&lt;/p&gt;

&lt;p&gt;So I split it like this: &lt;strong&gt;run ffmpeg exactly once at upload time, re-encoding into a form fdk-aac can handle while normalizing the container and metadata.&lt;/strong&gt; From there on, the pipeline assumes only normalized mp4 flows through it.&lt;/p&gt;

&lt;p&gt;In other words, ffmpeg wasn't eliminated. &lt;strong&gt;The heavy, potentially-unstable re-encoding work was pushed out of the repeatedly-executed transcription path and into a one-time pre-processing step at upload.&lt;/strong&gt; No matter how many times transcription runs, no matter how many retries fire, the re-encode never runs again. The hot path only ever deals with clean, standard input, free from edge cases.&lt;/p&gt;

&lt;p&gt;Looking back, this was the same idea as severing the memory multiplication: &lt;strong&gt;push heavy and unstable work out of the repeatedly-executed path into a one-time pre-process, and keep the hot path light and predictable.&lt;/strong&gt; The same principle ran through both the memory problem and the input-handling problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Paid Off
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory and Cost
&lt;/h3&gt;

&lt;p&gt;Peak memory stopped depending on file size, and memory spikes vanished in the vast majority of cases. It wasn't just that OOM went away — because the peak became constant, the container stopped dying even with a &lt;em&gt;lower&lt;/em&gt; memory allocation, which translated straight into cost savings. The "raise memory ⇄ crash" tug-of-war itself disappeared.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transcription Accuracy
&lt;/h3&gt;

&lt;p&gt;This was a side effect I hadn't aimed for, and it was a big one. By dropping fixed-length splitting and streaming the audio as a continuous stream, &lt;strong&gt;the problem of utterances getting chopped at chunk boundaries disappeared by construction.&lt;/strong&gt; The API can process with context intact, so dropped or misrecognized words straddling boundaries went away. The accuracy problem the previous engineer had been desperately balancing with the number 15 simply resolved itself as a byproduct of the design change.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Parameters Disappeared
&lt;/h3&gt;

&lt;p&gt;Both split length and concurrency had been parameters demanding tightrope tuning. With the ceiling on peak memory lowered, the three-way tradeoff of "instances I want to run ⇄ concurrency ⇄ memory safety" loosened all at once, and &lt;strong&gt;neither needed nervous adjustment anymore.&lt;/strong&gt; Rather than tweaking values as a symptomatic fix, I changed the structure of the problem — so the very things needing adjustment ceased to exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests Became Writable
&lt;/h3&gt;

&lt;p&gt;Modest but meaningful: testability. The old setup depended on ffmpeg as an external process, so verifying it meant actually launching that process — effectively only possible via E2E. Slow, fragile, environment-dependent.&lt;/p&gt;

&lt;p&gt;In the new setup, both demux and fdk-aac decoding became in-process library calls, so I could verify units like "feed this input, get this chunk, get this decode result" with unit tests. In practice it was still E2E-only at the time, and expanding unit test coverage remained on the to-do list — but at least the structure now &lt;em&gt;allowed&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;Lined up side by side — control over memory, ease of mid-stream resumption, testability — these were all consequences of one thing: &lt;strong&gt;pulling state that had been held by an external process back into my own code.&lt;/strong&gt; A single decision brought several good things along with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Calls and the Tradeoffs I Accepted
&lt;/h2&gt;

&lt;p&gt;It wasn't all a tidy success story. Some things I consciously settled for.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;fdk-aac as the decoder.&lt;/strong&gt; It was what I landed on after searching for an AAC decoder usable from cgo at the time. Pure-Go implementations exist, but many cap out at the AAC-LC profile, and I had no confidence they'd cover the variety of input. cgo complicates the build, so I prepared the native dependency as a Docker image and pulled only the build artifact in via multi-stage build at Cloud Run deploy time, eliminating the need to recompile fdk-aac on every build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The normalization phase has its own cost.&lt;/strong&gt; Re-encoding involves audio codec conversion, so if it's lossy the audio quality degrades once (whether that's within a range that doesn't affect transcription accuracy is worth examining). Storing the normalized file means holding it alongside the original, and it inserts an extra step between upload and transcription-ready. I judged the payoff of a stable hot path to be worth more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No support for formats other than mp4&lt;/strong&gt; — by design from the start. Input was a domain restricted to mp4, so I avoided over-abstraction (YAGNI).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not writing the demuxer from scratch&lt;/strong&gt; was deliberate too. Container parsing is a minefield of edge cases; rolling my own would shoulder all the breakage risk. Riding on a mature third-party library sidestepped that minefield. Working at a low level wasn't the goal — running stably was.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The lesson from this work is simple: &lt;strong&gt;if a parameter has to be tuned endlessly to stay stable, it may not be a value to adjust but a structure to remove.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 15-second split length, the carelessly-set concurrency, the up-and-down of the memory allocation — all of it was tug-of-war happening on top of one foundation: "load everything into memory, then process." The instant I changed the foundation to streaming, the tug-of-war itself vanished.&lt;/p&gt;

&lt;p&gt;And the idea of "pushing heavy, unstable work out of the repeatedly-executed path into a one-time pre-process" applies well beyond this one incident. Keep the hot path light and predictable. Reclaim, into your own code, the state an external process was holding. Do that, and memory, cost, and testability tend to follow.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>software</category>
      <category>performance</category>
    </item>
    <item>
      <title>Kyrage: A TypeScript-First Database Migration Tool for Modern Development</title>
      <dc:creator>Seiya Izumi</dc:creator>
      <pubDate>Sun, 14 Sep 2025 08:48:29 +0000</pubDate>
      <link>https://dev.to/izumisy/kyrage-a-typescript-first-database-migration-tool-for-modern-development-5272</link>
      <guid>https://dev.to/izumisy/kyrage-a-typescript-first-database-migration-tool-for-modern-development-5272</guid>
      <description>&lt;p&gt;When working on personal projects with CockroachDB, popular tools like Drizzle and Prisma presented unexpected challenges. Drizzle's CockroachDB support was still in planning stages, and while PostgreSQL compatibility seemed promising, migration attempts resulted in data type-related errors. Prisma had its own set of CockroachDB-specific issues.&lt;/p&gt;

&lt;p&gt;Most traditional Node.js migration tools require manually writing SQL migration files. What was missing was a tool that could automatically generate migrations based on schema differences while keeping everything in the TypeScript ecosystem - something that could provide the declarative approach similar to Ruby's ridgepole or Atlas, but with better integration for Node.js projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Kyrage
&lt;/h2&gt;

&lt;p&gt;This gap led to the creation of &lt;strong&gt;Kyrage&lt;/strong&gt; (kirāju) - a personal project designed to fill this specific need. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/IzumiSy" rel="noopener noreferrer"&gt;
        IzumiSy
      &lt;/a&gt; / &lt;a href="https://github.com/IzumiSy/kyrage" rel="noopener noreferrer"&gt;
        kyrage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A minimal, database-agnostic, schema-based declarative migration tool for Node.js ecosystem
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;kyrage&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/izumisy/kyrage/actions/workflows/test.yaml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/izumisy/kyrage/actions/workflows/test.yaml/badge.svg?branch=main" alt="Test"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/@izumisy/kyrage" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3150a1459296f2bcd7f9fd49ca931551aa227fddbda11158c6e5e5cd74bede6e/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f253430697a756d6973792532466b7972616765" alt="NPM Version"&gt;&lt;/a&gt;
&lt;a href="https://opensource.org/licenses/MIT" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href="https://nodejs.org/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d2e88424b16a3a4caa791f483565eb418bc9d45a48e28c25f3db9cc244629005/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d25334525334432322e782d627269676874677265656e2e737667" alt="Node.js Version"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A minimal, schema-based declarative migration tool for Node.js ecosystem&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;kyrage (kirāju)&lt;/strong&gt; automatically generates and applies database migrations by comparing your TypeScript schema definitions with your actual database state. No more writing migration files by hand!&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why kyrage?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Traditional database migrations require manually writing up/down migration files every time you change your schema. This is error-prone and time-consuming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;kyrage&lt;/strong&gt; takes a different approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✍️ Define your desired schema in TypeScript&lt;/li&gt;
&lt;li&gt;🔍 kyrage compares it with your actual database&lt;/li&gt;
&lt;li&gt;🚀 Automatically generates the necessary migrations&lt;/li&gt;
&lt;li&gt;✅ Apply migrations with a single command&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a style of managing database schema that is called as &lt;a href="https://atlasgo.io/blog/2022/08/11/announcing-versioned-migration-authoring" rel="nofollow noopener noreferrer"&gt;Versioned Migration Authoring&lt;/a&gt; by Atlas.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Supported Databases&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dialect&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;dialect&lt;/code&gt; value&lt;/th&gt;
&lt;th&gt;Dev Database&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;postgres&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Docker container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CockroachDB&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cockroachdb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Docker container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ File-based (no Docker required)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Dev Database container reuse feature (&lt;code&gt;kyrage dev start&lt;/code&gt;) is only available for PostgreSQL and…&lt;/p&gt;
&lt;/blockquote&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/IzumiSy/kyrage" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Since most development work happens in Next.js + TypeScript environments, the goal was to unify everything within a TypeScript codebase while providing a minimal, schema-based declarative migration tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Kyrage Works
&lt;/h2&gt;

&lt;p&gt;Kyrage automatically generates and applies database migrations by comparing your TypeScript schema definitions with your actual database state.&lt;/p&gt;

&lt;p&gt;The tool follows what Atlas calls the &lt;a href="https://entgo.io/blog/2022/03/14/announcing-versioned-migrations/" rel="noopener noreferrer"&gt;"Versioned Migration Authoring"&lt;/a&gt; approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect&lt;/strong&gt; to your database and introspect the current schema&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare&lt;/strong&gt; your TypeScript schema definition with the current state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate&lt;/strong&gt; a migration file (in JSON format) based on the differences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply&lt;/strong&gt; the migration automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Under the hood, kyrage leverages Kysely's migration functionality for SQL execution and version management, while providing its own CockroachDB dialect support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Schema Definition
&lt;/h3&gt;

&lt;p&gt;Define your database schema using intuitive TypeScript syntax. The schema definition benefits from Kysely's type system, providing excellent IDE autocompletion and type safety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;column&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineTable&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;t&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="s2"&gt;@izumisy/kyrage&lt;/span&gt;&lt;span class="dl"&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;const&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;members&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&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="na"&gt;primaryKey&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="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&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="na"&gt;unique&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&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="na"&gt;unique&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="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;integer&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="na"&gt;nullable&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="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timestamptz&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&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="na"&gt;primaryKey&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="na"&gt;author_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;published&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&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="na"&gt;default&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;published_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timestamptz&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="na"&gt;nullable&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kyrage also supports indexes, foreign key constraints, composite primary keys, and composite unique constraints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;author_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&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="na"&gt;notNull&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&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="na"&gt;notNull&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="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&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;// Composite primary key&lt;/span&gt;
    &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="s2"&gt;author_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;

    &lt;span class="c1"&gt;// Unique constraint with custom name&lt;/span&gt;
    &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author_id&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="s2"&gt;slug&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unique_author_slug&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="c1"&gt;// Foreign key with cascade delete&lt;/span&gt;
    &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="na"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cascade&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts_author_fk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;

    &lt;span class="c1"&gt;// Unique index&lt;/span&gt;
    &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slug&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="s2"&gt;title&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="na"&gt;unique&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="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dev Database Support
&lt;/h3&gt;

&lt;p&gt;Kyrage includes built-in mechanism to spin up ephemeral database for development as Docker container. This addresses common challenges in team development environments.&lt;/p&gt;

&lt;p&gt;To use this feature, add the &lt;code&gt;dev&lt;/code&gt; configuration to your kyrage config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres://postgres:password@localhost:5432/mydb&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="na"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres:17&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="na"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&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;Then generate migrations using the dev database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate migration using a clean, ephemeral database&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage generate &lt;span class="nt"&gt;--dev&lt;/span&gt;
🚀 Starting dev database &lt;span class="k"&gt;for &lt;/span&gt;migration generation...
✔ Dev database started: postgres
&lt;span class="nt"&gt;--&lt;/span&gt; create_table: &lt;span class="nb"&gt;users&lt;/span&gt;
✔ Migration file generated: migrations/1755525514175.json
✔ Dev database stopped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feature eliminates environment dependencies by removing the need to share production database credentials with developers. Each developer works with a clean slate, preventing conflicts that arise when multiple team members make schema changes simultaneously. The approach also reduces the risk of accidental production data modifications while ensuring consistency across the team since everyone uses the same Docker image.&lt;/p&gt;

&lt;p&gt;The implementation uses Testcontainers internally, making it seamless to run in CI/CD environments like GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation and Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install kyrage&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @izumisy/kyrage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create configuration file
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;kyrage.config.ts:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;defineConfig&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="s2"&gt;@izumisy/kyrage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;members&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&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="s2"&gt;./schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres://postgres:password@localhost:5432/mydb&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="na"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&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;h3&gt;
  
  
  Basic Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate migration&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npx @izumisy/kyrage generate
&lt;span class="nt"&gt;--&lt;/span&gt; create_table: members &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;, email, name, age, createdAt&lt;span class="o"&gt;)&lt;/span&gt; 
&lt;span class="nt"&gt;--&lt;/span&gt; create_table: posts &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;, author_id, title, content, published, published_at&lt;span class="o"&gt;)&lt;/span&gt;
✔ Migration file generated: migrations/1754372124127.json

&lt;span class="c"&gt;# Preview SQL before applying&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npx @izumisy/kyrage apply &lt;span class="nt"&gt;--plan&lt;/span&gt;
create table &lt;span class="s2"&gt;"members"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt; uuid not null primary key, &lt;span class="s2"&gt;"email"&lt;/span&gt; text not null unique, ...&lt;span class="o"&gt;)&lt;/span&gt;
create table &lt;span class="s2"&gt;"posts"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt; uuid not null primary key, &lt;span class="s2"&gt;"author_id"&lt;/span&gt; uuid not null, ...&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Apply migration&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npx @izumisy/kyrage apply
✔ Migration applied: 1754372124127
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Persistent Dev Databases
&lt;/h3&gt;

&lt;p&gt;Kyrage provides the ability to persist dev database containers across kyrage sessions, enabling applications to continuously connect to the same dev database container managed by kyrage&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start persistent dev database&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage dev start
✔ Applied 2 migrations
✨ Dev database ready: postgresql://postgres:password@localhost:32768/test

&lt;span class="c"&gt;# Get connection URL for your app&lt;/span&gt;
&lt;span class="nv"&gt;$ DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kyrage dev get-url&lt;span class="si"&gt;)&lt;/span&gt; npm run dev

&lt;span class="c"&gt;# Check running containers&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage dev status
Running: abc123def456 &lt;span class="o"&gt;(&lt;/span&gt;postgres:17&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Clean up when done&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage dev clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Migration Squashing
&lt;/h3&gt;

&lt;p&gt;For iterative development, kyrage supports squashing multiple migrations to clean up the migration history before applying to production. This feature treats the Dev Database like a feature branch - you can generate multiple migrations during development iterations, then squash them into a clean, consolidated migration when ready to merge to the main database.&lt;/p&gt;

&lt;p&gt;Here's how the workflow looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# During feature development - generate multiple migrations&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage generate &lt;span class="nt"&gt;--dev&lt;/span&gt;
✔ Migration file generated: migrations/001_add_users_table.json

&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage generate &lt;span class="nt"&gt;--dev&lt;/span&gt;  
✔ Migration file generated: migrations/002_add_email_column.json

&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage generate &lt;span class="nt"&gt;--dev&lt;/span&gt;
✔ Migration file generated: migrations/003_add_email_index.json

&lt;span class="c"&gt;# Before merging to main - squash into a single clean migration&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage generate &lt;span class="nt"&gt;--squash&lt;/span&gt;
✔ Squashed 3 migrations into: migrations/004_user_management_feature.json

&lt;span class="c"&gt;# Apply the clean, squashed migration to production&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage apply
✔ Migration applied: 004_user_management_feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps organize changes before applying them to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment-Specific Configuration
&lt;/h3&gt;

&lt;p&gt;Thanks to the &lt;a href="https://github.com/unjs/c12" rel="noopener noreferrer"&gt;unjs/c12&lt;/a&gt; configuration system, kyrage supports environment-specific settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;$development&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres://dev:dev@localhost:5432/myapp_dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres:17&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="na"&gt;$production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;connectionString&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;tables&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;h2&gt;
  
  
  Why Choose Kyrage?
&lt;/h2&gt;

&lt;p&gt;Kyrage offers several compelling advantages over traditional migration tools. Its TypeScript-first design keeps everything in your familiar development ecosystem with excellent IDE support and type safety. The declarative approach means you define your desired database state and kyrage figures out how to get there, eliminating the tedious process of writing manual migration files. &lt;/p&gt;

&lt;p&gt;The Dev Database support removes common team development friction by providing isolated environments for each developer. Rather than reinventing database migration fundamentals, kyrage builds upon proven tools like Kysely while adding modern conveniences that align with contemporary development practices.&lt;/p&gt;

&lt;p&gt;And let's be honest - if you're building applications with CockroachDB in the TypeScript ecosystem, kyrage is pretty much your only option that actually works! While other popular tools struggle with CockroachDB compatibility, kyrage was specifically designed with this use case in mind, thanks to Kysely's excellent dialect extensibility!&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript All the Way
&lt;/h2&gt;

&lt;p&gt;Kyrage is designed as a minimal tool focused solely on schema migrations - it doesn't include query building capabilities. This intentional design pairs perfectly with Kysely and kysely-codegen to create the ultimate TypeScript database development experience.&lt;/p&gt;

&lt;p&gt;The recommended workflow combines kyrage for schema management with kysely-codegen for type-safe query building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Spin up Dev Database to get url&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage dev start

&lt;span class="c"&gt;# Update your schema and generate migration (automatically apply the changes)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kyrage generate &lt;span class="nt"&gt;--dev&lt;/span&gt;

&lt;span class="c"&gt;# Generate type-safe query builder types from your updated schema&lt;/span&gt;
&lt;span class="nv"&gt;$ DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kyrage dev get-url&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; kysely-codegen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combination gives you declarative schema management through kyrage while leveraging Kysely's powerful type-safe query building capabilities. Other similar libraries that generate type-safe query builders from database schemas work equally well in this setup, allowing you to choose the tools that best fit your project's needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Roadmap
&lt;/h2&gt;

&lt;p&gt;The kyrage project has several exciting features planned that will significantly enhance the developer experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Export API:&lt;/strong&gt; Reverse-engineer existing databases into TypeScript schemas - perfect for migrating legacy projects to kyrage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-database support:&lt;/strong&gt; Expanding to DuckDB, SQLite, and MySQL for broader ecosystem compatibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks API:&lt;/strong&gt; Execute custom logic during migrations (automated backups, notifications, CI/CD integration)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seed data pre-population:&lt;/strong&gt; Automatically populate development databases with test data for streamlined development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features aim to make kyrage not just a migration tool, but a complete database development workflow solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Involved
&lt;/h2&gt;

&lt;p&gt;Kyrage is open source and ready for you to try! Whether you're tired of writing migration files by hand, struggling with CockroachDB compatibility, or just want a cleaner TypeScript-first database workflow, kyrage might be exactly what you've been looking for. &lt;/p&gt;

&lt;p&gt;The project welcomes contributors, feedback, and real-world usage stories as it continues to evolve.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>typescript</category>
      <category>database</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Loading state design revisited in Elm</title>
      <dc:creator>Seiya Izumi</dc:creator>
      <pubDate>Sat, 15 Aug 2020 14:58:30 +0000</pubDate>
      <link>https://dev.to/izumisy/loading-state-design-revisited-in-elm-4f9a</link>
      <guid>https://dev.to/izumisy/loading-state-design-revisited-in-elm-4f9a</guid>
      <description>&lt;p&gt;If you are a frontend dev, I suppose that you have been frequently through the scene that you implement loading view in the frontend application. &lt;/p&gt;

&lt;p&gt;Elm is a domain-specific language built for frontend applications, so it is pretty usual to design loading view. The great thing is that, Elm has &lt;code&gt;Maybe&lt;/code&gt; that says if the value is available or not in a type level. Using &lt;code&gt;Maybe&lt;/code&gt; in designing loading state in Elm is really naive pattern, but this is legit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&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&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bookmarks&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;Bookmarks&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, we can implement &lt;code&gt;init&lt;/code&gt; function as follows. &lt;code&gt;Nothing&lt;/code&gt; is a variant that says no value available, so we can understant that this function returns empty Model anyway.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;init&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&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bookmarks&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As I said this is a naive pattern, this Model design is failure in an Elm way.&lt;/p&gt;

&lt;p&gt;First of all, &lt;code&gt;Maybe&lt;/code&gt; does not carry any of context information. &lt;code&gt;Maybe&lt;/code&gt; is just a type says it can be &lt;code&gt;Nothing&lt;/code&gt; or &lt;code&gt;Just&lt;/code&gt;, so it does not tell you why it is &lt;code&gt;Maybe&lt;/code&gt;. This is meta, but the bigger your codebase gets, the more critical how to convey the context of it by code is. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Maybe&lt;/code&gt; is way better than Boolean, but using it too many times is almost equal to Boolean Identity Crisis.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8Af1bh-BVY8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;h1&gt;
  
  
  Unleash Custom Type
&lt;/h1&gt;

&lt;p&gt;Let the code tell us its context. This is exactly what Elm as a typed language should be, and where Custom Type comes into.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;None&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;UserLoaded&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;BookmarksLoaded&lt;/span&gt; &lt;span class="kt"&gt;Bookmarks&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;AllLoaded&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="kt"&gt;Bookmarks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Our Model is now way more descriptive than before. We can understand that bookmarks and an user are needed to be waited until fetched.&lt;/p&gt;

&lt;p&gt;In addition to the descriptivity, this also has advantage that &lt;strong&gt;we don't have to explicitly unwrap them out to non-Maybe types anymore!&lt;/strong&gt; If every single data waited to be fetched is all wrapped with &lt;code&gt;Maybe&lt;/code&gt;, it bothers us that we always have to unwrap it every single time even at where we are sure that it would never be &lt;code&gt;Nothing&lt;/code&gt;. We can now extract all data just by a single pattern matching of Model.&lt;/p&gt;

&lt;p&gt;I have learnt this pattern from the video, "Make Data Structure" by Richard Feldman at Elm Europe 2018.&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.amazonaws.com%2Fi%2Frjmrlunzj84yvkl7m02k.jpg" 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.amazonaws.com%2Fi%2Frjmrlunzj84yvkl7m02k.jpg" alt="A page of Richard's slide " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=x1FU3e0sT1I" rel="noopener noreferrer"&gt;Make Data Structure by Richard Feldman&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Pitfall: Combinatorial Explosion
&lt;/h1&gt;

&lt;p&gt;However, we would easily come across the case that designing Model simply with Custom Type does not actually work out.&lt;/p&gt;

&lt;p&gt;Imagine that some more additional data sources are needed to be fetched in initialization. Working with two is still duable by designing your Model with a simple Custom Type like the previous, but how about more of it? The bigger the number gets like three, four, five, the harder it is to cover all their patterns up.&lt;/p&gt;

&lt;p&gt;This is a kind of combinatorial explosion. If you design waiting state on your Model which waits 3 resources at the intialization.&lt;code&gt;Maybe&lt;/code&gt; has cardinaliy of 2 which is the number of exponent of waiting resources, so the result is 3^2=9. Now we can see that the number of variants on your model is 9. It exceeds 9 if additional error handling is needed. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The number of variants can easily be exploded.&lt;/strong&gt; Using a simple Custom Type in desiging waiting state in your Model is totally unrealistic if it is more than two.&lt;/p&gt;
&lt;h1&gt;
  
  
  elm-multi-waitable
&lt;/h1&gt;

&lt;p&gt;As one of the solution for the variant explosion in Model, I crafted a small package that encapsulate multiple &lt;code&gt;Maybe&lt;/code&gt;s from Model. This package is well tested on our product built with Elm.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/IzumiSy" rel="noopener noreferrer"&gt;
        IzumiSy
      &lt;/a&gt; / &lt;a href="https://github.com/IzumiSy/elm-multi-waitable" rel="noopener noreferrer"&gt;
        elm-multi-waitable
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A small package like a traffic light
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The great thing this package exactly improve is your Model design that needs waiting multiple resources. If your application has to wait 3 resources at the initialization, your Model will look like as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; 
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;MultiWaitable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Wait3&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="kt"&gt;Options&lt;/span&gt; &lt;span class="kt"&gt;Bookmarks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Loaded&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="kt"&gt;Options&lt;/span&gt; &lt;span class="kt"&gt;Bookmarks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very neat! Even if we need more of resources waiting, the variants never be exploded anymore.&lt;/p&gt;

&lt;p&gt;The rest that wires everything up is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Loading&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="kt"&gt;MultiWaitable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init3&lt;/span&gt; &lt;span class="kt"&gt;AllFinished&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AllFinished&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="kt"&gt;Options&lt;/span&gt; &lt;span class="kt"&gt;Bookmarks&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;UserFetched&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;OptionsFetched&lt;/span&gt; &lt;span class="kt"&gt;Options&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;BookmarksFetched&lt;/span&gt; &lt;span class="kt"&gt;Bookmarks&lt;/span&gt;


&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt; &lt;span class="n"&gt;waitable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;UserFetched&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;waitable&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;MultiWaitable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait3Update1&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt;

        &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt; &lt;span class="n"&gt;waitable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;UserOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;waitable&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;MultiWaitable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait3Update2&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt;

        &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt; &lt;span class="n"&gt;waitable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;BookmarksFetched&lt;/span&gt; &lt;span class="n"&gt;bookmarks&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;waitable&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;MultiWaitable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait3Update3&lt;/span&gt; &lt;span class="n"&gt;bookmarks&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapFirst&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt;

        &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Loading&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;AllFinished&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="n"&gt;bookmarks&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="kt"&gt;Loaded&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="n"&gt;bookmarks&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MultiWaitable module essentially provides statemachine-like functionality. Internal implementation of this package actually uses multiple &lt;code&gt;Maybe&lt;/code&gt;s to keep track of status of what has been done and what still not, but it is well encupsalated.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wait[N]Update[N]&lt;/code&gt; function MultiWaitable provides returns a tuple contains updated model and Cmd which publishes a completion Msg registered by &lt;code&gt;init[N]&lt;/code&gt; function that initialize MultiWaitable at first. Once all waiting state internally kept is marked as completed, &lt;code&gt;wait[N]Update[N]&lt;/code&gt; function emits a Cmd for the completion Msg.&lt;/p&gt;

&lt;p&gt;What this package brings developers is exaclty a great concentration on Model designing itself. You would be able to design your Model in a descriptive way without any contextless, distractful multiple &lt;code&gt;Maybe&lt;/code&gt;s.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cleaner model, greater maitainability
&lt;/h1&gt;

&lt;p&gt;Model is exactly the place where has the bigger portion of clue to know how your application works at a glance, &lt;strong&gt;but it gets massive in no time when the application grows.&lt;/strong&gt; This is no doubt.&lt;/p&gt;

&lt;p&gt;Clean model does not let you misunderstand your application and reduce bugs in  implementing new features. Designing loading state in a clean way is one of the easiest things we can tackle right now.&lt;/p&gt;

&lt;p&gt;Always keep eyes on your Model and let it have enough context.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Designing Opaque Type for form fields in Elm: Part 2</title>
      <dc:creator>Seiya Izumi</dc:creator>
      <pubDate>Sun, 12 Apr 2020 12:35:59 +0000</pubDate>
      <link>https://dev.to/izumisy/designing-opaque-type-for-form-fields-in-elm-part-2-251j</link>
      <guid>https://dev.to/izumisy/designing-opaque-type-for-form-fields-in-elm-part-2-251j</guid>
      <description>&lt;p&gt;In the previous article, we implemented &lt;code&gt;Username&lt;/code&gt; module which encapsulates validation logic of form fields.&lt;/p&gt;

&lt;p&gt;That's really enough at that moment, but it still has more room to get improved in viewpoint of extensibility. &lt;/p&gt;

&lt;p&gt;Say, if we would like to implement one more another field of &lt;code&gt;Email&lt;/code&gt; that have almost the same logic of &lt;code&gt;Username&lt;/code&gt;, but with different validation rule. At some point, we can follow rules of &lt;strong&gt;WET (Write Everything Twice)&lt;/strong&gt;. Abstract something out too early is always a way to ruin your application. So, it would be better to be dumb about it just by cloning functions of &lt;code&gt;Username&lt;/code&gt; into &lt;code&gt;Email&lt;/code&gt; module. Then, module relation will be as follows.&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.amazonaws.com%2Fi%2F212z8wiac1iob2us2t3h.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.amazonaws.com%2Fi%2F212z8wiac1iob2us2t3h.png" alt="Module relation diagram" width="212" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks fine for now, but getting stinky. Being WET is nice, but don't always be wet. As solution toward this kind of case, we can take advantage of &lt;strong&gt;module composition&lt;/strong&gt; in order to make modules highly coherent and reusable without breaking border of responsibility each module has. This is really similar to class inheritance, but way better than that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Explicitness and Implicitness
&lt;/h1&gt;

&lt;p&gt;Elm does not have class-based syntax so that naturally we always will take step to use module composition to extend behavior of one another module. This is a nice thing that we don't have any chance to get into pitfalls of class inheritance.&lt;/p&gt;

&lt;p&gt;Class inheritance is usually seemed to be &lt;strong&gt;implicit&lt;/strong&gt;, because the all public behaviour of parent classes is carried over by a child class, sometimes intentionally, but sometimes not. This means class inhertance is not suitable to pick only some specfic behaviours of other classes.&lt;/p&gt;

&lt;p&gt;Composition seems way &lt;strong&gt;explicit&lt;/strong&gt; on the contrary. If we don't have to inherit some specific functionality, just ignore it. It even has chance to inherit behaviours of compositing classes just by delegation. Simple and concise.&lt;/p&gt;

&lt;p&gt;Using module composition in Elm is a key to make your modules extensible and reusable all the way. Extracting business rules in your application into Opaque Type is nice to decouple interface and implementation. Module composition can empower it to be reusable in a good manner.&lt;/p&gt;

&lt;h1&gt;
  
  
  Overall design
&lt;/h1&gt;

&lt;p&gt;Now, let's apply module composition into our application.&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.amazonaws.com%2Fi%2Fbvu60vv8vyq16x41nlx5.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.amazonaws.com%2Fi%2Fbvu60vv8vyq16x41nlx5.png" alt="overall design" width="493" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right before using composition, each module had its own implementation intendedly duplicated. However, now, their internal implementations are delegated to &lt;code&gt;Field&lt;/code&gt; module that has fundamental implementation abstracted out of &lt;code&gt;Username&lt;/code&gt; and &lt;code&gt;Email&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Field module
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;Field&lt;/code&gt; module now has three states initially &lt;code&gt;Username&lt;/code&gt; had before.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;validator&lt;/code&gt; field in &lt;code&gt;Common&lt;/code&gt; record is important. That is defined as a function interface that gets injected in &lt;code&gt;init&lt;/code&gt; function. The reason why &lt;code&gt;validator&lt;/code&gt; interface requires implementation to return &lt;code&gt;( String, err )&lt;/code&gt; tuple is that, even though validation fails, the current value is needed to be accessible to show it on input field. If it has only &lt;code&gt;err&lt;/code&gt;, the value typed by users will be lost. This must not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Common&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Common&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Valid&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Common&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Common&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Partial&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Of course, &lt;code&gt;Field&lt;/code&gt; module has &lt;code&gt;input&lt;/code&gt; function and &lt;code&gt;blur&lt;/code&gt; function as well, but they are more abstract.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mapErrorToString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attributes&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Events&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onBlur&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;-- ...&lt;/span&gt;

&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;onInputMsg&lt;/span&gt; &lt;span class="n"&gt;onBlurMsg&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;onInputHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;onInputMsg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                        &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="kt"&gt;Partial&lt;/span&gt;
                                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
                                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;
                                &lt;span class="p"&gt;}&lt;/span&gt;

                        &lt;span class="kt"&gt;Valid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="n"&gt;value&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mapResult&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

                        &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="n"&gt;value&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;
                                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mapResult&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text"&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onBlur&lt;/span&gt; &lt;span class="n"&gt;onBlurMsg&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="n"&gt;onInputHandler&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;


&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mapResult&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;


&lt;span class="n"&gt;mapErrorToString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;mapErrorToString&lt;/span&gt; &lt;span class="n"&gt;translator&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;translator&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;

        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Some more internal functions&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- internals&lt;/span&gt;


&lt;span class="n"&gt;mapResult&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="n"&gt;mapResult&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;validator_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;validator&lt;/span&gt;

                &lt;span class="kt"&gt;Valid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;validator&lt;/span&gt;

                &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;validator&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Ok&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Valid&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator_&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Invalid&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator_&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;err&lt;/span&gt;


&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;

        &lt;span class="kt"&gt;Valid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;

        &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, let's look into how to use this &lt;code&gt;Field&lt;/code&gt; module under the way of module composition in order to create specialized modules.&lt;/p&gt;
&lt;h1&gt;
  
  
  Username module
&lt;/h1&gt;

&lt;p&gt;As the way of module composition, almost all exposed function delegates its functionality to internal dependent module. &lt;code&gt;Username&lt;/code&gt; module is just a wrapper of &lt;code&gt;Field&lt;/code&gt; in this meaning that provides specialized behaviours extended from &lt;code&gt;Field&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;One more great thing is that, &lt;strong&gt;interface of &lt;code&gt;Username&lt;/code&gt; module is almost not changed since the last article&lt;/strong&gt; even though its internal functionality is updated and being delegated! Actually, &lt;code&gt;errorString&lt;/code&gt; function was newly introduced in place of &lt;code&gt;error&lt;/code&gt; function that is described in the last article, but this is just an optional change. &lt;/p&gt;

&lt;p&gt;Everything stays same overall. The stability of module interface is always important to avoid unintentional effect to other functionality.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Field&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;LengthTooLong&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;LengthTooShort&lt;/span&gt;


&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
        &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
            &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                    &lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;LengthTooLong&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                    &lt;span class="kt"&gt;Err&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;LengthTooShort&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="kt"&gt;Ok&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;input&lt;/code&gt; function and &lt;code&gt;blur&lt;/code&gt; function are, as they exactly show, just does delagation to functions provided by &lt;code&gt;Field&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;errorString&lt;/code&gt; is a little different. It works like a translator of &lt;code&gt;Error&lt;/code&gt; type in this &lt;code&gt;Username&lt;/code&gt; module. Error patterns vary on every module extensing &lt;code&gt;Field&lt;/code&gt; so that &lt;code&gt;Username&lt;/code&gt; module must have responsibility of translating error type to &lt;code&gt;String&lt;/code&gt; message as a specialized module.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorString&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;-- ...&lt;/span&gt;

&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;onInputMsg&lt;/span&gt; &lt;span class="n"&gt;onBlurMsg&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onInputMsg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;onBlurMsg&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;


&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;


&lt;span class="n"&gt;errorString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
&lt;span class="n"&gt;errorString&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapErrorToString&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;LengthTooShort&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Length is too short"&lt;/span&gt;

                &lt;span class="kt"&gt;LengthTooLong&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Length is too long"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This article has shown only the example of &lt;code&gt;Username&lt;/code&gt; type, but with this strategy, it would be way easier to implement some similar types like &lt;code&gt;Email&lt;/code&gt;, &lt;code&gt;Biography&lt;/code&gt; that need validation like this. That's because essential logics to help us implement it as fields are now reusable.&lt;/p&gt;

&lt;p&gt;Complete example is on Github.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/IzumiSy" rel="noopener noreferrer"&gt;
        IzumiSy
      &lt;/a&gt; / &lt;a href="https://github.com/IzumiSy/elm-compositional-form-field" rel="noopener noreferrer"&gt;
        elm-compositional-form-field
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Typed form implementation in Elm
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>elm</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Designing Opaque Type for form fields in Elm: Part 1</title>
      <dc:creator>Seiya Izumi</dc:creator>
      <pubDate>Wed, 08 Apr 2020 14:36:53 +0000</pubDate>
      <link>https://dev.to/izumisy/designing-opaque-type-for-form-fields-in-elm-4299</link>
      <guid>https://dev.to/izumisy/designing-opaque-type-for-form-fields-in-elm-4299</guid>
      <description>&lt;p&gt;This is an article that shares my knowledge on designing Opaque Type for form fields.&lt;/p&gt;

&lt;p&gt;Let's say, we have this kind of a simple form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- A simple String value form&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; 
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UsernameInputted&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;


&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;UsernameInputted&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty much fine for now, but one day we feel like to add validation to &lt;code&gt;username&lt;/code&gt; that it must be more than 5 characters and less than 20 characters. &lt;/p&gt;

&lt;h1&gt;
  
  
  Opaque Type for validations
&lt;/h1&gt;

&lt;p&gt;Now, this is the time to use Opaque Type to encapsulate validation logic into the separated module. We introduce &lt;code&gt;Username&lt;/code&gt; type which wraps &lt;code&gt;String&lt;/code&gt; as its internal data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;


&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="kt"&gt;Nothing&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="kt"&gt;Nothing&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="n"&gt;value&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;new&lt;/code&gt; function now here is a point that does validation of inputted &lt;code&gt;String&lt;/code&gt;. If the inputted data is valid, it returns &lt;code&gt;Just&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Okay, now it seems that we can start using &lt;code&gt;Username&lt;/code&gt; as a field value like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it is impossible actually. &lt;/p&gt;

&lt;h1&gt;
  
  
  Design failure
&lt;/h1&gt;

&lt;p&gt;The main reason is time to run validation. In current design, we have validation mechanism in &lt;code&gt;new&lt;/code&gt; function so that it always works when &lt;code&gt;Username&lt;/code&gt; data is initialized with &lt;code&gt;String&lt;/code&gt; value. The time to initialize  it is that &lt;code&gt;update&lt;/code&gt; function handling message attached to &lt;code&gt;onInput&lt;/code&gt; event coming from views, so &lt;code&gt;Username&lt;/code&gt; initialization is going to be triggered every single time when users type their keyboard to fill the field.&lt;/p&gt;

&lt;p&gt;It means that, even though the users don't finish typing their value on the field, &lt;strong&gt;validation errors will pop up, because the value they are typing partially to fill the field is always invalid&lt;/strong&gt;. Errors will appear from the beginning. This is so annoying!&lt;/p&gt;

&lt;p&gt;So, in case of this, we need to change timing to trigger off validation. The thing to keep in mind is that, &lt;strong&gt;Opaque Type for input fields will have "partial" state which describes "users don't need to get validation at this moment"&lt;/strong&gt;. Don't start validation from the very beginning, but do it right once users finished their typing on the field.&lt;/p&gt;

&lt;p&gt;Let's see how we can make it better!&lt;/p&gt;

&lt;h1&gt;
  
  
  Improvement
&lt;/h1&gt;

&lt;p&gt;Now, &lt;code&gt;Username&lt;/code&gt; type has three patterns. Every variant has String that describes the current value of it. When the data is &lt;code&gt;Partial&lt;/code&gt;, it means that the data conveyed on it does not get validated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Valid&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;LengthTooLong&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;LengthTooShort&lt;/span&gt;


&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;
&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;

        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="kt"&gt;Nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The intitial state of &lt;code&gt;Username&lt;/code&gt; is always &lt;code&gt;Partial&lt;/code&gt; as &lt;code&gt;empty&lt;/code&gt; functions shows. It will not be changed until users finished filling value of it. &lt;/p&gt;

&lt;p&gt;Then, when is &lt;code&gt;Partial&lt;/code&gt; changed into other two variants? That's exactly what &lt;code&gt;view&lt;/code&gt; and &lt;code&gt;blur&lt;/code&gt; function do.&lt;/p&gt;

&lt;p&gt;The moment to start validation is when users removed focus from input fields, so this module provides &lt;code&gt;blur&lt;/code&gt; function for it. &lt;code&gt;blur&lt;/code&gt; function is expected to be used in &lt;code&gt;update&lt;/code&gt; function handling &lt;code&gt;onBlur&lt;/code&gt; event. It triggers off validation at once.&lt;/p&gt;

&lt;p&gt;On the contrary, the handler for &lt;code&gt;onInput&lt;/code&gt; event does not trigger validation during &lt;code&gt;Partial&lt;/code&gt; is given to it. It always is just waiting that &lt;code&gt;blur&lt;/code&gt; function gets called. Once validation has started by &lt;code&gt;blur&lt;/code&gt; function, the handler for &lt;code&gt;onInput&lt;/code&gt; triggers off validation every time!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Browser&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attributes&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Events&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onBlur&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;-- ...&lt;/span&gt;


&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;onInputMsg&lt;/span&gt; &lt;span class="n"&gt;onBlurMsg&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;onInputHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;onInputMsg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                        &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

                        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                            &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text"&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onBlur&lt;/span&gt; &lt;span class="n"&gt;onBlurMsg&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt; &lt;span class="n"&gt;onInputHandler&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;


&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;username&lt;/span&gt;


&lt;span class="c1"&gt;-- internals&lt;/span&gt;


&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;
&lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kt"&gt;LengthTooLong&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kt"&gt;LengthTooShort&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="kt"&gt;Valid&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Partial&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;

        &lt;span class="kt"&gt;Valid&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;

        &lt;span class="kt"&gt;Invalid&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;validate&lt;/code&gt; function and &lt;code&gt;toString&lt;/code&gt; function are pretty simple. They two probably have no need to be described. One is just to do validation, and the other is to extract &lt;code&gt;String&lt;/code&gt; from &lt;code&gt;Username&lt;/code&gt; type.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wire it up!
&lt;/h1&gt;

&lt;p&gt;Now, we don't have to care timing or anything in converting a primitive value of &lt;code&gt;String&lt;/code&gt; into &lt;code&gt;Username&lt;/code&gt;. It all is completely handled by &lt;code&gt;Username&lt;/code&gt; module itself. Gee!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="kt"&gt;App&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UsernameInputted&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Username&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;UsernameBlurred&lt;/span&gt;


&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;UsernameInputted&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;UsernameBlurred&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blur&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;
                &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="n"&gt;div&lt;/span&gt;
        &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="kt"&gt;UsernameInputted&lt;/span&gt; &lt;span class="kt"&gt;UsernameBlurred&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Program&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sandbox&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One-file demo on Ellie is &lt;a href="https://ellie-app.com/8xs2jZmhfGSa1" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elm</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why I created validatable-record</title>
      <dc:creator>Seiya Izumi</dc:creator>
      <pubDate>Sat, 18 Nov 2017 05:11:42 +0000</pubDate>
      <link>https://dev.to/izumisy/why-i-created-validatable-record-c4d</link>
      <guid>https://dev.to/izumisy/why-i-created-validatable-record-c4d</guid>
      <description>&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/IzumiSy" rel="noopener noreferrer"&gt;
        IzumiSy
      &lt;/a&gt; / &lt;a href="https://github.com/IzumiSy/validatable-record" rel="noopener noreferrer"&gt;
        validatable-record
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Immutable.js Record powered with validate.js
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;validatable-record&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://dl.circleci.com/status-badge/redirect/gh/IzumiSy/validatable-record/tree/master" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/45f10138db03dd2a6bca89d205090fc5cdb5777267c1559185db6f3de0096331/68747470733a2f2f646c2e636972636c6563692e636f6d2f7374617475732d62616467652f696d672f67682f497a756d6953792f76616c6964617461626c652d7265636f72642f747265652f6d61737465722e7376673f7374796c653d737667" alt="CircleCI"&gt;&lt;/a&gt;
&lt;a href="https://github.com/RichardLitt/standard-readme" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d19ebf41dec11a62d6fe98c4a424d843f22b923d8b5f67df26de01af26e38c39/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374616e646172642d2d726561646d652d4f4b2d677265656e2e7376673f7374796c653d666c61742d737175617265" alt="standard-readme compliant"&gt;&lt;/a&gt;
&lt;a href="https://badge.fury.io/js/validatable-record" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e936e4d707237c778624b024a1add32a319bb02457698bfadd8d560d704a3cc8/68747470733a2f2f62616467652e667572792e696f2f6a732f76616c6964617461626c652d7265636f72642e737667" alt="npm version"&gt;&lt;/a&gt;
&lt;a href="https://github.com/IzumiSy/validatable-recordLICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/95c10fa6088c05deb9bdfe215245185f6f8262c17e68b55188cfcf7c3ce03c52/687474703a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c6174" alt="MIT License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Immutable.js Record powered with validate.js&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/IzumiSy/validatable-recordREADME.md#Install" rel="noopener noreferrer"&gt;Install&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/IzumiSy/validatable-recordREADME.md#Usage" rel="noopener noreferrer"&gt;Usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/IzumiSy/validatable-recordREADME.md#Contribute" rel="noopener noreferrer"&gt;Contribute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/IzumiSy/validatable-recordREADME.md#Test" rel="noopener noreferrer"&gt;Test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/IzumiSy/validatable-recordREADME.md#License" rel="noopener noreferrer"&gt;License&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ npm install --save validatable-record&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;ValidatableRecord returns &lt;code&gt;Record&lt;/code&gt; in Immutable.js for extending your own class. Usage is almost the same as &lt;code&gt;Record&lt;/code&gt; in Immutable.js, but it has the power of &lt;code&gt;validate.js&lt;/code&gt;. With ValidatableRecord, you can define models with built-in validation logic.&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-v"&gt;ManRecord&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;ValidatableRecord&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;name&lt;/span&gt;: &lt;span class="pl-c1"&gt;null&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;age&lt;/span&gt;: &lt;span class="pl-c1"&gt;null&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;name&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;presence&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;age&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;presence&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
      &lt;span class="pl-c1"&gt;message&lt;/span&gt;: &lt;span class="pl-s"&gt;"is invalid"&lt;/span&gt;
    &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-v"&gt;Man&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-v"&gt;ManRecord&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  ...
&lt;span class="pl-kos"&gt;}&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;man&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;Man&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;"Justine"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  age: &lt;span class="pl-c1"&gt;25&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-s1"&gt;man&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;validate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c"&gt;// == true&lt;/span&gt;

&lt;span class="pl-c"&gt;// Of course you can use `Immutable.Record` methods&lt;/span&gt;
&lt;span class="pl-s1"&gt;man&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;size&lt;/span&gt;        &lt;span class="pl-c"&gt;// 2&lt;/span&gt;
&lt;span class="pl-s1"&gt;man&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;get&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'name'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c"&gt;// "Justine"&lt;/span&gt;
&lt;span class="pl-s1"&gt;man&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/IzumiSy/validatable-record" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;A few month ago, I created a small module composed of &lt;a href="https://facebook.github.io/immutable-js/" rel="noopener noreferrer"&gt;Immutable.js&lt;/a&gt; and &lt;a href="https://validatejs.org/" rel="noopener noreferrer"&gt;validate.js&lt;/a&gt;. This is a kind of implementation of my approach to the thought about where do you put your validation logic in a front-end application. The field of massive front-end application architecture is totally different from your hobby apps if you value things like scalability or maintainability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Where to put your own validation logic in your application is not actually a small concern. However, at the beginning phase of front-end application development, the validation logic is often not seemed to be important. In this kind of situation, the view layer tends to have a role of validation, because it is pretty usual for lots of people to think that validation is only the job of view layer.&lt;/p&gt;

&lt;p&gt;There is no correct answer to &lt;em&gt;“Where should I put my own validation logic?”&lt;/em&gt;, because it really depends on cases, but the sure thing is that the validation logic in view layer should not be the one which relates to your business rule.&lt;/p&gt;

&lt;p&gt;Let’s think about a common validation rule like &lt;em&gt;“Your name must not contain special characters”&lt;/em&gt;. It is mostly a sort of requirement by infrastructure layer which usually includes things like database, external API server, and so on. However, the validations like “You cannot order this item more than one” or “Free shipping is not available in your membership” are different. These are unique to business rules of your own application. Uniqueness is that, whether the rule is required only because of your business rule or not.&lt;/p&gt;

&lt;p&gt;Business rules as domain layer are never affected by other ones such as view layer and infrastructure layer, because view and infrastructure are just implementations which provide your own business as a software and implementations never change what your business rule is. In this concept of layered architecture, validation logic in almost all cases is better to be a part of domain layer. Validation is one of the business rules which compose your application as a domain, not as infrastructure or view.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;validatable-record&lt;/code&gt; is a small module built with Immutable.js and validate.js in order to define immutable record model with validation rules. Unlike ActiveModel in RoR, there is no standard built-in model validation module in JavaScript, so almost all the time if you would like to put your validation logic in a part of domain model, you will have to write write your own implementation. In the situation, validatable-record is available as one approach to built-in model validation.&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="nx"&gt;ManRecord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ValidatableRecord&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;presence&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="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;presence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is 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="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// You can use ManRecord as Immutable.Record&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Man&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ManRecord&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;man&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Man&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Justine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// You can check if the instance is valid or not&lt;/span&gt;
&lt;span class="nx"&gt;man&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// == true&lt;/span&gt;

&lt;span class="c1"&gt;// Of course you can use `Immutable.Record` methods&lt;/span&gt;
&lt;span class="nx"&gt;man&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;        &lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;span class="nx"&gt;man&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// "Justine"&lt;/span&gt;
&lt;span class="nx"&gt;man&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// 25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ValidatableRecord&lt;/code&gt; returns &lt;code&gt;Immutable.Record&lt;/code&gt; enhanced with &lt;code&gt;validate.js&lt;/code&gt; by taking constraint rules as a second argument. Of course, you can still use methods in &lt;code&gt;Record&lt;/code&gt;. You can also get the given error message after validation. More about on &lt;a href=""&gt;https://www.npmjs.com/package/validatable-record&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What I would like to stress in this article with my approach of &lt;code&gt;validatable-record&lt;/code&gt; is, scaling front-end application sometimes needs serious planning on responsibility of every layers. Developing front-end application is getting easier than before today, but still software engineers are required to think deeply about architecture from the view point of scalability and maintainability. I would be grad if this article could take you one step back and re-think about your own great application architecture on front-end.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>architecture</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
