<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lunatech's engineer blog</title>
	<atom:link href="https://blog.lunatech.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.lunatech.com</link>
	<description>Simplify your IT</description>
	<lastBuildDate>Sun, 24 Jan 2021 09:46:02 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	weekly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>Lunatech's blog engine</generator>

<image>
	<url>https://blog.lunatech.com/assets/images/logo-lunatech-france.png</url>
	<title>Lunatech's engineer blog</title>
	<link>https://blog.lunatech.com</link>
	<width>32</width>
	<height>32</height>
</image> 

 
	<item>
		<title>The software-defined vehicle moves the work to the backend</title>
		<link>https://blog.lunatech.com//posts/2026-06-10-software-defined-vehicle-backend</link>
		
		<dc:creator><![CDATA[Titouan]]></dc:creator>
        <pubDate>2026-06-10T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[eclipse-sdv]]></category>
                }
             {
            <category><![CDATA[rust]]></category>
                }
             {
            <category><![CDATA[kuksa]]></category>
                }
             {
            <category><![CDATA[hawkbit]]></category>
                }
             {
            <category><![CDATA[mqtt]]></category>
                }
             {
            <category><![CDATA[axum]]></category>
                }
             {
            <category><![CDATA[software-defined-vehicle]]></category>
                }
             {
            <category><![CDATA[open-source]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-06-10-software-defined-vehicle-backend</guid>

					<description>
                        <![CDATA[ A modern car is a computer that happens to have wheels. The interesting code no longer lives in the engine bay. It lives on a server, talking to the vehicle over the network, deciding what the fleet knows and what each car runs next. The industry calls this the software-defined vehicle. The name hides where the real engineering goes: into the backend.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>A modern car is a computer that happens to have wheels. The interesting code no longer lives in the engine bay. It lives on a server, talking to the vehicle over the network, deciding what the fleet knows and what each car runs next. The industry calls this the software-defined vehicle. The name hides where the real engineering goes: into the backend.</p>
</div>
<div class="paragraph">
<p>We built a working demo to make that point concrete. It manages a fleet of twenty vehicles. Live GPS positions flow from each car to a map in the browser. Software updates roll out over the air from a panel in the UI. The whole thing starts with one <code>docker compose up</code> and runs on a laptop.</p>
</div>
<div class="paragraph">
<p>It is open source. You can read the code here: <a href="https://github.com/lunatech-labs/sdv-fleet-management">lunatech-labs/sdv-fleet-management</a>. This post walks the data path end to end, then explains why the server side is written in Rust.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_shape_of_the_system">The shape of the system</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The architecture is a pipeline. Vehicle state originates in per-vehicle Eclipse Kuksa Databrokers, crosses an Eclipse Mosquitto MQTT broker, lands in a Rust backend, and reaches the browser over a WebSocket. Over-the-air campaigns run on a parallel track through Eclipse HawkBit down to per-vehicle update agents.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="../media/2026-06-10-software-defined-vehicle-backend/architecture.png" alt="SDV Fleet Management architecture: the data path">
</div>
</div>
<div class="paragraph">
<p>Three Eclipse SDV projects do the heavy lifting, and we adopted them rather than inventing private equivalents. Kuksa holds vehicle state. HawkBit runs the update campaigns. Mosquitto moves the messages. Building on an open ecosystem in the open is itself a form of contribution, and it is the honest first step toward upstream work.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_stage_one_kuksa_and_the_vehicle_signal_specification">Stage one: Kuksa and the Vehicle Signal Specification</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Each of the twenty cars runs its own Kuksa Databroker. A Databroker is a small gRPC service that holds the current value of a vehicle&#8217;s signals, addressed by the Vehicle Signal Specification (VSS). VSS is a tree. A position is not a bespoke field, it is <code>Vehicle.CurrentLocation.Latitude</code> and <code>Vehicle.CurrentLocation.Longitude</code>. Identity is <code>Vehicle.VehicleIdentification.VIN</code>, <code>.Brand</code>, <code>.Model</code>.</p>
</div>
<div class="paragraph">
<p>The contract is gRPC, so the signals are typed and discoverable. You can read a signal straight from a broker with <code>grpcurl</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">grpcurl -plaintext \
  -d '{"entries":[{"path":"Vehicle.CurrentLocation.Latitude","fields":["FIELD_VALUE"]}]}' \
  localhost:55556 kuksa.val.v1.VAL/Get</code></pre>
</div>
</div>
<div class="paragraph">
<p>Twenty brokers means twenty independent sources of truth, one per VIN, each listening on its own port. That mirrors reality: a fleet is not one database, it is many vehicles that each know their own state.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_stage_two_the_sidecar_that_bridges_grpc_to_mqtt">Stage two: the sidecar that bridges gRPC to MQTT</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Kuksa speaks gRPC. The rest of the pipeline speaks MQTT. Something has to bridge the two, and that is the <code>kuksa2mqtt</code> sidecar, written in Rust, one instance per vehicle.</p>
</div>
<div class="paragraph">
<p>The sidecar is the source of each car&#8217;s movement. It runs a GPS random walk, writes the new latitude and longitude into the Databroker over gRPC once per second, and publishes the same pair to MQTT on a per-vehicle topic. Identity is different. VIN, brand, and model do not change, so the sidecar publishes them once at startup. The topic structure carries the VIN and the VSS path, so a subscriber can filter exactly what it wants:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>kuksa/VIN-0001/telemetry/CurrentLocation/Latitude    48.8571
kuksa/VIN-0001/telemetry/CurrentLocation/Longitude   2.3529
kuksa/VIN-0001/telemetry/VehicleIdentification/Brand Toyota</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is a deliberate seam. The backend never talks gRPC to the cars. It subscribes to one wildcard topic and lets the broker fan in the whole fleet. Adding a vehicle means starting another Databroker and another sidecar. The backend code does not change.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_stage_three_the_axum_backend">Stage three: the axum backend</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The backend is an axum service on port 3000. On startup it subscribes to a single wildcard:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>kuksa/+/telemetry/#</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>+</code> matches any VIN, the <code>#</code> matches any signal path below it. One subscription, the entire fleet. As messages arrive the backend updates an in-memory view of fleet state, keyed by VIN. There is no database in the hot path. The current position of a car is a value in a map, protected for concurrent access, updated as fast as MQTT delivers.</p>
</div>
<div class="paragraph">
<p>The REST surface is thin and predictable:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>GET  /fleet                 snapshot of all vehicles
GET  /vehicles/{vin}        one vehicle
GET  /versions              available software versions
GET  /campaigns             campaign list and state
POST /campaigns             launch a rollout</code></pre>
</div>
</div>
<div class="paragraph">
<p>The handler signatures read the way axum encourages: typed extractors in, typed responses out. Launching a campaign takes a body with a target version and a list of VINs, and returns the created campaign or a typed error.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-rust" data-lang="rust">#[derive(Debug, Deserialize, ToSchema)]
pub struct CreateCampaign {
    pub version: String,
    pub vins: Vec&lt;String&gt;,
}

pub async fn create_campaign(
    State(state): State&lt;AppState&gt;,
    Json(req): Json&lt;CreateCampaign&gt;,
) -&gt; Result&lt;Json&lt;Campaign&gt;, (StatusCode, Json&lt;ApiError&gt;)&gt; {
    // start the HawkBit rollout, fold it into campaign state, return the record
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>ToSchema</code> derive is not decoration. It is what feeds the OpenAPI generator, and we will come back to it.</p>
</div>
<div class="paragraph">
<p>The browser does not poll for live data. It asks once over REST for the initial snapshot, then opens a WebSocket and listens. The frames are small Rust types serialised to JSON. The position event is exactly three fields:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-rust" data-lang="rust">#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PositionEvent {
    pub vin: String,
    pub lat: f64,
    pub lon: f64,
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>On the wire the two streams look like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>// /ws/fleet
{"vin":"VIN-0003","lat":48.8641,"lon":2.3318}

// /ws/campaigns
{"campaign_id":"...","vin":"VIN-0001","state":"Installing"}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The map moves because the server tells it to, not because it keeps asking. This is the single most important property of the read path: state changes are pushed, never polled, and the wire format is small enough to be obvious at a glance.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_ota_track_hawkbit_and_a_state_machine_per_vehicle">The OTA track: HawkBit and a state machine per vehicle</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Updates run on their own path. When you launch a campaign from the UI, the backend asks HawkBit to start a rollout for the chosen version and target VINs. HawkBit owns the Direct Device Integration (DDI) protocol: each OTA agent, one per vehicle and written in Rust, polls HawkBit for assigned actions, downloads, installs, and reports progress.</p>
</div>
<div class="paragraph">
<p>Every vehicle moves through its own state machine:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>Pending -&gt; Downloading -&gt; Installing -&gt; Complete
                                     -&gt; Failed</code></pre>
</div>
</div>
<div class="paragraph">
<p>The agent reports each transition to HawkBit over DDI. The backend polls HawkBit for progress, folds it into campaign state, and pushes it to the UI over the WebSocket, where the map marker changes colour. Two tracks reach the same screen: telemetry arrives over MQTT, campaign state comes from HawkBit. Neither is polled by the browser.</p>
</div>
<div class="paragraph">
<p>One detail we kept on purpose: every OTA agent fails its update twenty percent of the time, deliberately. A demo that always succeeds teaches nothing about the system you would actually run. Real fleets have cars with bad connections and interrupted downloads. The interesting code is the code that handles the unhappy path. So some markers turn red, the agent reports the failure to HawkBit, and the state machine records it instead of pretending the rollout went clean. A rollout you can trust is one that tells you when it breaks.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_why_rust_on_the_server">Why Rust on the server</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The data path from car to screen is Rust from end to end: the sidecars, the backend, the update agents. That is not an accident. The reasons are concrete.</p>
</div>
<div class="paragraph">
<p>Concurrency without surprises. The backend holds shared fleet state while MQTT messages, REST requests, and WebSocket pushes all touch it at once. Rust&#8217;s ownership model turns the data races you would chase at runtime in another stack into compile errors. The borrow checker is doing fleet-state integrity for free.</p>
</div>
<div class="paragraph">
<p>Typed integration boundaries. Every seam in this system is a place where a wrong assumption costs you. A VSS path, an MQTT payload, a HawkBit response, a JSON frame on the wire. Modelling each as a Rust type means a malformed message fails at the edge, with a clear error, instead of propagating a bad value toward a vehicle.</p>
</div>
<div class="paragraph">
<p>A small, predictable footprint. axum on Tokio gives an async runtime with no garbage collector pauses and a memory profile flat enough to run twenty sidecars, twenty agents, and a backend on a laptop without thinking about it.</p>
</div>
<div class="paragraph">
<p>The API contract cannot drift. The OpenAPI specification is generated directly from the Rust source with <code>utoipa</code>: the same <code><mark>[derive(ToSchema)]</code> on a type and the <code></mark>[utoipa::path(&#8230;&#8203;)]</code> attribute on a handler that the compiler checks are what produce the spec. The Swagger explorer at <code>/docs</code> is never out of date with the running server, because there is no second source of truth to fall out of sync. The types that define the handlers are the types that define the spec.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">open http://localhost:3000/docs   # generated from the Rust source</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_how_it_was_built">How it was built</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We built this with Claude as a coding partner. The repository carries its working context in a file at the root, so the assistant understood the architecture, the conventions, and the constraints on every session. The result is not a prototype held together by hand. The Rust side runs <code>cargo fmt</code>, <code>cargo clippy&#8201;&#8212;&#8201;-D warnings</code>, and <code>cargo test</code> on every push. The frontend lints and unit-tests. A Playwright end-to-end suite drives the full stack. All of it runs in continuous integration.</p>
</div>
<div class="paragraph">
<p>That is the part worth noting for anyone weighing AI-assisted development. The leverage is real, but it shows up as discipline, not shortcuts. The tests, the generated API contract, and the clean separation between services are what let a small team move fast without leaving a mess behind.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_see_it_run">See it run</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Twenty vehicle pins are spread across Paris, each moving on its own at one update per second. Green pins are up to date, red pins are out of date or have a campaign in progress.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="../media/2026-06-10-software-defined-vehicle-backend/01-fleet-map.png" alt="Live fleet map with twenty vehicles moving across Paris">
</div>
</div>
<div class="paragraph">
<p>A full walkthrough: the live map, the vehicle drawer, the fleet table, an over-the-air campaign launch, and the state updates landing in real time.</p>
</div>
<div class="videoblock">
<div class="content">
<iframe width="960" height="540" src="https://www.youtube.com/embed/HokMqkx5VmI?rel=0" frameborder="0" allowfullscreen></iframe>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_run_it_yourself">Run it yourself</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">git clone git@github.com:lunatech-labs/sdv-fleet-management.git
cd sdv-fleet-management
cp .env.example .env   # set HAWKBIT_TOKEN, HAWKBIT_USER, HAWKBIT_PASSWORD
docker compose up</code></pre>
</div>
</div>
<div class="paragraph">
<p>Open the map at <code><a href="http://localhost:8080" class="bare">http://localhost:8080</a></code>. Twenty cars move across Paris at one update per second. Click one to read its VIN, brand, model, and software version, all served from the single <code>/fleet</code> snapshot. Launch a campaign and watch the markers change colour as each vehicle walks its own state machine. Open a second terminal and <code>websocat ws://localhost:3000/ws/fleet</code> to read the raw frames the browser is consuming.</p>
</div>
<div class="paragraph">
<p>The software-defined vehicle is a backend problem wearing a car. The Eclipse SDV projects give the industry a shared foundation to build on. We are building on it, in the open, and we would rather show the work than describe it.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>LunaBet: shipping a Panini-style World Cup pool in Rust, in an afternoon</title>
		<link>https://blog.lunatech.com//posts/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust</link>
		
		<dc:creator><![CDATA[Nicolas Leroux]]></dc:creator>
        <pubDate>2026-05-30T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[rust]]></category>
                }
             {
            <category><![CDATA[axum]]></category>
                }
             {
            <category><![CDATA[htmx]]></category>
                }
             {
            <category><![CDATA[three.js]]></category>
                }
             {
            <category><![CDATA[world cup]]></category>
                }
             {
            <category><![CDATA[side project]]></category>
                }
             {
            <category><![CDATA[ai orchestration]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust</guid>

					<description>
                        <![CDATA[ Every four years the same conversation happens in our Lunatech Slack: who is going to run the office pool for the World Cup? Every four years someone says "I will do it in a spreadsheet", and every four years that spreadsheet ends up as a corrupted Google Sheet with three formulas fighting over the same cell. So this time, between two cups of coffee, I decided to build the thing properly. The result is https://lunabet.eu[LunaBet], and the Lunatech-internal instance lives at https://lunatech.lunabet.eu[lunatech.lunabet.eu].]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Every four years the same conversation happens in our Lunatech Slack: who is going to run the office pool for the World Cup? Every four years someone says "I will do it in a spreadsheet", and every four years that spreadsheet ends up as a corrupted Google Sheet with three formulas fighting over the same cell. So this time, between two cups of coffee, I decided to build the thing properly. The result is <a href="https://lunabet.eu">LunaBet</a>, and the Lunatech-internal instance lives at <a href="https://lunatech.lunabet.eu">lunatech.lunabet.eu</a>.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/background.png" alt="LunaBet landing page">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_story_in_one_sentence">The story in one sentence</h2>
<div class="sectionbody">
<div class="paragraph">
<p>LunaBet is a small multi-tenant betting app for the FIFA World Cup 2026. Pick the exact score of every match, score points (3 for the exact score, 1 for the right winner), climb the leaderboard, optionally chip in 2, 5 or 10 euros to a shared pot that the top 3 split at the end of the group stage. There is one twist: the whole thing is themed like a Panini sticker album that took a wrong turn through a <a href="https://en.wikipedia.org/wiki/Captain_Tsubasa">Captain Tsubasa</a> rerun, with the obligatory <em>"the ball is your friend"</em> quote from Olivier Atton.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_multi_tenant_by_design">Multi-tenant by design</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The interesting bit is that LunaBet is not a single-tenant Lunatech tool. Any organization can sign up at <a href="https://lunabet.eu">lunabet.eu</a>, pick a slug, and immediately get their own branded space at <code>&lt;slug&gt;.lunabet.eu</code>. The only restriction is that your colleagues have to sign in with an email that matches your organization&#8217;s domain, which keeps the pool internal without any user management on your side. Lunatech itself is just one of many possible tenants: <code>lunatech.lunabet.eu</code> is for people with an <code>@lunatech.com</code> email, but a friend in another shop can spin up <code>acme.lunabet.eu</code> for <code>@acme.com</code> in less than a minute.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_tour_of_the_app">Tour of the app</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The login flow is a magic link, so there are no passwords to remember and no SSO to configure. Type your work email, click the link, and you are in.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/02-login.png" alt="Magic link sign-in">
</div>
</div>
<div class="paragraph">
<p>The matches page is the place where you spend most of your time during the tournament. Each match is a Panini-style sticker card with the two flags, the kick-off time, and a tiny inline form to predict the score. Bets close at kick-off, so no insider trading once the players are on the pitch.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/04-matches.png" alt="Matches page with the Panini stickers">
</div>
</div>
<div class="paragraph">
<p>The leaderboard updates automatically as soon as a match is final. The 3D goal-shot scene at the top is rendered in the browser with Three.js, full goal cage with posts, crossbar and a translucent net, a parabolic top-corner shot, a flash and a bounce, the whole thing looping every few seconds. It is gratuitous and we love it.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/05-leaderboard.png" alt="Leaderboard with the 3D goal scene">
</div>
</div>
<div class="paragraph">
<p>If you want skin in the game, you can join the pot for 2, 5 or 10 euros. The app never touches the money: you Lydia or bank-transfer the admin, the admin marks you as paid, and the pot is shared between the top three paid players at the end of the group stage. Higher stakes get a bigger slice of their position&#8217;s share, which means a 10 euro player in third still walks away with something interesting.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/06-stake.png" alt="Choosing a stake tier">
</div>
</div>
<div class="paragraph">
<p>Admins get a small back office to mark payments as received and to settle the pot.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/07-admin-stakes.png" alt="Admin stakes management">
</div>
</div>
<div class="paragraph">
<p>Everything is bilingual, French and English, switchable from the topbar. The leaderboard in English looks like this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/08-leaderboard-en.png" alt="English leaderboard">
</div>
</div>
<div class="paragraph">
<p>The transactional emails follow the same theme: navy header, a striped pitch, a red <em>stamp</em> button. Magic-link emails are localized to the visitor&#8217;s language at request time, while match reminders are bilingual side by side because we do not store a per-user language.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/09-email-magic-link-fr.png" alt="Magic link email" width="French version">
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/10-email-magic-link-en.png" alt="Magic link email" width="English version">
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-30-lunabet-a-panini-style-world-cup-pool-in-rust/11-email-reminder.png" alt="Bilingual match reminder">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_stack_and_why">The stack, and why</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The codebase is intentionally boring. The whole app is one Rust binary built with Axum, with templates compiled into the binary via Askama, htmx for the tiny bits of client interactivity, Three.js loaded from a CDN for the 3D ball, and lettre for the SMTP side of magic links and reminders. Persistence is PostgreSQL through SQLx, with migrations applied at startup so deployment is just <em>drop the binary and restart</em>. Match fixtures and results are pulled from <a href="https://www.football-data.org">football-data.org</a> every five minutes, which is also when the reminder job fires.</p>
</div>
<div class="paragraph">
<p>That last point is more interesting than it sounds: football-data.org exposes hundreds of competitions behind a single competition code, so spinning up a pool for a new tournament is essentially a one-line change. <code>FOOTBALL_DATA_COMPETITION=WC</code> gives you the World Cup, <code>EC</code> gives you the Euros, <code>CL</code> the Champions League, <code>PL</code> the Premier League, and so on. Restart the binary and the fixtures, results and reminders for the next competition flow in on their own. The Panini sticker theme and the scoring rules do not care which tournament is in season, so the same instance can be repurposed for whatever your team wants to bet on next.</p>
</div>
<div class="paragraph">
<p>Rust was not a bet, it was the path of least resistance. A single statically linked binary, no runtime to install on the server, no garbage collection pauses to worry about, and the type system catches the kind of off-by-one mistakes that ruin scoring logic when you are coding at 11 pm. Axum and SQLx are mature enough that you spend zero time fighting the framework and all your time on the actual problem, which in our case was "make exact-score betting fun".</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_how_fast_can_you_actually_ship">How fast can you actually ship?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>LunaBet is also, quietly, an example of the kind of "orchestrating, not coding" story I <a href="https://blog.lunatech.com/posts/2026-05-19-from-coding-to-orchestrating">wrote about a few weeks ago</a>. The first usable version, with magic links, scoring, leaderboard, fixtures sync and emails, came together in a single afternoon and an evening. Not because the AI did it, and not because I am particularly fast at Rust, but because the context was right: a clean problem statement, a target stack chosen up front, a Panini moodboard pinned to the brief, and a tight feedback loop between writing and testing. The model wrote a lot of the scaffolding. I wrote the rules, the constraints, and the look. Then I read every line.</p>
</div>
<div class="paragraph">
<p>The multi-tenant work, the per-org branding, the bilingual emails and the dev-mode one-click sign-in came in a second pass once the core was solid. Same recipe: small, well-scoped tasks; verify each output; commit when green.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_try_it">Try it</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If your team has been threatening to redo The Spreadsheet, just send them to <a href="https://lunabet.eu">lunabet.eu</a>. Pick a slug, confirm by email, and your office pool is live before the kick-off of the opener. The code is open source under Apache 2.0 at <a href="https://github.com/lunatech-labs/lunatech-lunabet">github.com/lunatech-labs/lunatech-lunabet</a> if you would rather self-host or fork it.</p>
</div>
<div class="paragraph">
<p>One legal note before you do: a real-money pool between colleagues, even when settled outside the app on the honor system, can fall under your local gambling regulation. In France it is the ANJ. Run the idea past HR or legal before any public rollout. LunaBet never touches the money, which limits exposure, but it does not eliminate it.</p>
</div>
<hr>
<div class="paragraph">
<p><em>Contact: <a href="mailto:nicolas.leroux@lunatech.com">nicolas.leroux@lunatech.com</a> | <a href="https://lunatech.com">lunatech.com</a></em></p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Legacy Modernization with AI: A Battle-Tested Methodology</title>
		<link>https://blog.lunatech.com//posts/2026-05-26-legacy-modernization-with-ai</link>
		
		<dc:creator><![CDATA[Nicolas Leroux]]></dc:creator>
        <pubDate>2026-05-26T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[legacy modernization]]></category>
                }
             {
            <category><![CDATA[AI methodology]]></category>
                }
             {
            <category><![CDATA[agentic coding]]></category>
                }
             {
            <category><![CDATA[context pack]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-05-26-legacy-modernization-with-ai</guid>

					<description>
                        <![CDATA[ In the previous articles, we established three things: inaction is expensive, AI is collapsing the cost of building software, and engineers are becoming orchestrators rather than instrumentalists. The natural question is: how do you put all of this together into a real engagement, with real stakes, real data, and zero tolerance for regression?]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In the previous articles, we established three things: inaction is expensive, AI is collapsing the cost of building software, and engineers are becoming orchestrators rather than instrumentalists. The natural question is: how do you put all of this together into a real engagement, with real stakes, real data, and zero tolerance for regression?</p>
</div>
<div class="paragraph">
<p>This article reveals the methodology we have been building and refining at Lunatech over the past two years: Legacy Modernization with AI.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_operating_principle">The operating principle</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Everything in our framework starts from a single, non-negotiable principle:</p>
</div>
<div class="quoteblock">
<blockquote>
For the same input, under the same preconditions, the new system produces exactly the same observable output as the legacy system.
</blockquote>
</div>
<div class="paragraph">
<p>100% functional parity. Verified, not assumed. This is not a best-effort goal&#8201;&#8212;&#8201;it is the exit criterion. Every phase, every deliverable, and every quality gate is designed to serve this principle.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_three_commitments">Three commitments</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We make three commitments on every engagement.</p>
</div>
<div class="paragraph">
<p>First, <strong>100% functional parity</strong>. We use behavioral equivalence testing and parallel running to prove that the new system matches the old one. Parity is verified through automated comparison, not through manual testing or optimistic assumptions.</p>
</div>
<div class="paragraph">
<p>Second, <strong>a reproducible methodology</strong>. The same seven-phase framework, the same deliverables, the same quality gates&#8201;&#8212;&#8201;on every engagement, regardless of size. Consistency removes guesswork and makes outcomes predictable.</p>
</div>
<div class="paragraph">
<p>Third, <strong>AI as a first-class tool</strong>. We use agentic coding with Claude Code on every engagement, supported by a dedicated AI engineer who curates context for every task. AI is not an afterthought or an experiment. It is central to how we deliver.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_seven_phases">The seven phases</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Our framework follows seven phases, each with clear deliverables and exit gates.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-26-legacy-modernization-with-ai/methodology.png" alt="The 7-Phase Legacy Modernization Methodology">
</div>
</div>
<div class="sect2">
<h3 id="_phase_1_discover_1_2_weeks">Phase 1: Discover (1-2 weeks)</h3>
<div class="paragraph">
<p>Stakeholder interviews, system mapping, and the creation of the first version of the context pack. The goal is to understand what the system does, who depends on it, and what "done" looks like for this engagement.</p>
</div>
</div>
<div class="sect2">
<h3 id="_phase_2_reverse_engineer_1_3_weeks">Phase 2: Reverse-Engineer (1-3 weeks)</h3>
<div class="paragraph">
<p>Claude Code scans the legacy codebase, extracts business rules, and maps dependencies. The AI engineer captures golden datasets&#8201;&#8212;&#8201;curated input/output pairs that define the system&#8217;s expected behavior. This phase produces the living documentation the legacy system never had.</p>
</div>
</div>
<div class="sect2">
<h3 id="_phase_3_re_architect_1_2_weeks">Phase 3: Re-Architect (1-2 weeks)</h3>
<div class="paragraph">
<p>The target stack is defined, architecture decision records (ADRs) are written, and the migration strategy is formalized. The delivery plan is priced. Clients can stop at this phase boundary with a complete modernization blueprint in hand.</p>
</div>
</div>
<div class="sect2">
<h3 id="_phase_4_rebuild_4_10_weeks">Phase 4: Rebuild (4-10 weeks)</h3>
<div class="paragraph">
<p>This is where agentic coding does the heavy lifting. Modules are translated one by one using Claude Code, with each translation verified against the golden dataset. Equivalence tests run in CI on every commit. The context pack grows with every module completed.</p>
</div>
</div>
<div class="sect2">
<h3 id="_phase_5_migrate_2_4_weeks">Phase 5: Migrate (2-4 weeks)</h3>
<div class="paragraph">
<p>Data is classified into four categories&#8201;&#8212;&#8201;reference data, master data, transactional data, and in-flight data&#8201;&#8212;&#8201;each with its own migration path, validation criteria, and rollback plan. Parallel running begins: production inputs are routed to both the legacy and target systems, and a reconciliation engine compares outputs field by field, row by row.</p>
</div>
</div>
<div class="sect2">
<h3 id="_phase_6_harden_1_3_weeks">Phase 6: Harden (1-3 weeks)</h3>
<div class="paragraph">
<p>Penetration testing, load testing, observability wiring, and compliance evidence gathering. Everything that makes a system production-ready beyond functional correctness.</p>
</div>
</div>
<div class="sect2">
<h3 id="_phase_7_handover_1_2_weeks">Phase 7: Handover (1-2 weeks)</h3>
<div class="paragraph">
<p>The context pack is transferred to the client&#8217;s team. Training covers developers, operators, and business stakeholders. An AI-in-engineering enablement program teaches the client&#8217;s team to use Claude Code effectively for continued evolution. A 30-day hypercare period is included.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p><strong>Scale bands:</strong> Small engagements (4-6 weeks), Mid-size (2.5-6 months), Large (4-8 months).</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_claude_context_pack">The Claude Context Pack</h2>
<div class="sectionbody">
<div class="paragraph">
<p>At the heart of the methodology is the Claude Context Pack&#8201;&#8212;&#8201;a versioned folder in the project repository, treated with the same rigor as production code. It contains seven layers:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><strong>System</strong></dt>
<dd>
<p>Plain-language description of what the system does.</p>
</dd>
<dt class="hdlist1"><strong>Code</strong></dt>
<dd>
<p>Modules, responsibilities, dependencies (AI-generated).</p>
</dd>
<dt class="hdlist1"><strong>Data</strong></dt>
<dd>
<p>Schemas, data dictionaries, golden I/O samples.</p>
</dd>
<dt class="hdlist1"><strong>Business</strong></dt>
<dd>
<p>Domain glossary, business rules in plain text.</p>
</dd>
<dt class="hdlist1"><strong>Architecture</strong></dt>
<dd>
<p>Target stack, coding standards, NFRs, security defaults.</p>
</dd>
<dt class="hdlist1"><strong>Test</strong></dt>
<dd>
<p>Equivalence test patterns, behaviors to preserve.</p>
</dd>
<dt class="hdlist1"><strong>Decision</strong></dt>
<dd>
<p>ADRs, open questions, non-negotiables.</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>A dedicated AI engineer builds, curates, and maintains the context pack throughout the engagement. Context review happens at every phase gate. The pack grows richer with each phase, and every AI task draws from it.</p>
</div>
<div class="paragraph">
<p>Importantly, Claude never gets the full pack at once. For each task, a task-specific brief is assembled from the relevant slices. Translating a module? The agent receives the legacy code, the business rules, the target architecture, the equivalence tests, and a completed example. Writing a migration script? It receives schemas, transformation rules, and golden datasets for verification.</p>
</div>
<div class="paragraph">
<p>This is the secret to consistent quality: targeted context produces precise output.</p>
</div>
<div class="paragraph">
<p>After handover, the context pack stays with the client as living documentation the legacy system never had, onboarding material for new developers, the foundation for continued AI use, and traceable architecture decisions. The system finally has the documentation it should have always had.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_how_parity_is_verified">How parity is verified</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Parity verification follows the Input/Output Equivalence Model. Production inputs are routed to both the legacy and target systems. A reconciliation engine compares outputs and classifies every variance into one of three categories:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>A <strong>parity defect</strong> blocks go-live and must be fixed.</p>
</li>
<li>
<p>A <strong>known legacy defect corrected in the target</strong> is a signed-off exception where the new system is intentionally better.</p>
</li>
<li>
<p>An <strong>acceptable minor difference</strong> is documented and approved.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Daily reconciliation reports track progress. Cut-over is rehearsed in staging with explicit rollback steps. Go-live happens only when the reconciliation report shows zero unresolved parity defects.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_why_small_teams_win">Why small teams win</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Our team model is deliberately lean: 2-4 senior engineers, one dedicated AI engineer, and one project lead. This is not a cost-cutting measure&#8201;&#8212;&#8201;it is a performance optimization.</p>
</div>
<div class="paragraph">
<p>The human bottleneck in software projects is not production. It is coordination, review, and judgment. Doubling headcount doubles overhead: more standups, more merge conflicts, more context-switching, slower decisions. Small teams of senior engineers working with Claude Code outperform larger teams on every metric that matters.</p>
</div>
<div class="paragraph">
<p>The AI engineer is a role unique to our methodology. This person curates context, maintains the context pack, and optimizes AI workflows daily. They are the guarantee that AI output quality stays high throughout the engagement.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_five_metrics_we_track">Five metrics we track</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We measure progress with five metrics, all auditable and reported weekly.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 66.6667%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Metric</th>
<th class="tableblock halign-left valign-top">Definition</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Module Coverage</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">% of legacy modules with a target equivalent passing equivalence tests</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Context Pack Completeness</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">% of the 7 layers populated and reviewed by the AI engineer</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Golden Dataset Coverage</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">% of identified scenarios with curated input/output pairs</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Equivalence Test Pass Rate</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">% of golden-dataset test cases passing on the target system</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Reconciliation Variance</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">% of parallel-run transactions with zero variance</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Every metric is measurable, auditable, and reported weekly. There is no ambiguity about where the project stands at any point.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_you_get_at_the_end">What you get at the end</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When the engagement is complete, clients walk away with a modern, cloud-native system with 100% verified functional parity. They get predictable cost through per-phase pricing (fixed price or capped T&amp;M)&#8201;&#8212;&#8201;and they can stop at any phase boundary. They receive a future-ready stack, ready for continued evolution with AI-augmented workflows. And they keep the context pack as a permanent asset: the documentation, the knowledge, and the foundation for their team to keep evolving.</p>
</div>
<div class="paragraph">
<p>A modernization that leaves you unable to evolve has only solved half the problem. We solve both halves.</p>
</div>
<hr>
<div class="paragraph">
<p><em>This is the fifth and final article in the series "Software Will Cost Almost Nothing. What Happens Next?" If you are considering a legacy modernization project and want to explore what AI-accelerated delivery could look like for your organization, get in touch.</em></p>
</div>
<div class="paragraph">
<p><em>Contact: <a href="mailto:nicolas.leroux@lunatech.com">nicolas.leroux@lunatech.com</a> | <a href="https://lunatech.com">lunatech.com</a></em></p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>From Coding to Orchestrating: The New Role of the Software Engineer</title>
		<link>https://blog.lunatech.com//posts/2026-05-19-from-coding-to-orchestrating</link>
		
		<dc:creator><![CDATA[Nicolas Leroux]]></dc:creator>
        <pubDate>2026-05-19T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[software engineering]]></category>
                }
             {
            <category><![CDATA[AI orchestration]]></category>
                }
             {
            <category><![CDATA[context quality]]></category>
                }
             {
            <category><![CDATA[spec-driven development]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-05-19-from-coding-to-orchestrating</guid>

					<description>
                        <![CDATA[ There is a widespread fantasy about AI in software development. It goes like this: you tell the AI to rewrite your entire banking system in Kotlin, press a button, go to lunch, and come back to a perfect application.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>There is a widespread fantasy about AI in software development. It goes like this: you tell the AI to rewrite your entire banking system in Kotlin, press a button, go to lunch, and come back to a perfect application.</p>
</div>
<div class="paragraph">
<p>That is not how it works. And understanding why is the most important career insight for any engineer today.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_it_is_not_magic">It is not magic</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Without structure, AI produces plausible but generic output that requires extensive rework. Ask it to "rewrite this COBOL module in Java" and you will get syntactically correct code with wrong field names, missing business rules, no error handling patterns, and a result that needs 80% rework. It looks impressive in a demo. It fails in production.</p>
</div>
<div class="paragraph">
<p>The difference between success and failure lies entirely in methodology. AI is extraordinarily powerful, but it is not autonomous in any meaningful sense. It does not know your domain, your constraints, your naming conventions, your business rules, or your definition of "done." It needs to be told. And the quality of what you tell it determines the quality of what you get back.</p>
</div>
<div class="paragraph">
<p>This is captured in a single equation that governs everything:</p>
</div>
<div class="paragraph lead">
<p><strong>AI Output Quality = AI Capability x Context Quality</strong></p>
</div>
<div class="paragraph">
<p>AI capability improves every quarter. You cannot control it. That is Anthropic&#8217;s job, or OpenAI&#8217;s, or Google&#8217;s. Context quality is the lever you own. This is where engineering discipline makes the difference.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conductors_not_instrumentalists">Conductors, not instrumentalists</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The metaphor that best captures the new role of the software engineer is the orchestra conductor. A conductor does not play every instrument. They set the tempo, the dynamics, and the interpretation. They ensure that dozens of musicians play together coherently, that the quiet passage builds properly into the crescendo, that the timing is precise.</p>
</div>
<div class="paragraph">
<p>Similarly, the modern software engineer does not write every line of code. They orchestrate AI agents&#8201;&#8212;&#8201;setting the context, defining the constraints, breaking work into coherent tasks, verifying each output, and accumulating knowledge over time.</p>
</div>
<div class="paragraph">
<p>The new core skills are: preparing the right context, defining quality gates, structuring work for AI consumption, verifying output rigorously, and curating knowledge so it compounds over time.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_a_tale_of_two_prompts">A tale of two prompts</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The difference between naive and engineered AI use is dramatic.</p>
</div>
<div class="paragraph">
<p>The <strong>naive prompt</strong> says: "Rewrite this COBOL module in Java." What you get back is generic Java code with wrong field names, missing business rules, no error handling patterns, and a result that needs 80% rework.</p>
</div>
<div class="paragraph">
<p>The <strong>engineered prompt</strong> says: "Rewrite COBOL module X following target architecture Y, with these business rules, these naming conventions, these test cases, and these equivalence criteria." What you get back is code that is correct on the first pass, matches the target architecture, uses domain-specific naming, and passes equivalence tests. It needs 5-10% review.</p>
</div>
<div class="paragraph">
<p>The delta between these two outcomes is not the AI model. It is the context.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_context_means_in_practice">What context means in practice</h2>
<div class="sectionbody">
<div class="paragraph">
<p>At Lunatech, we have formalized context into seven layers that together form what we call the Claude Context Pack:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-19-from-coding-to-orchestrating/context-layers.png" alt="Context Layer">
</div>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><strong>System</strong></dt>
<dd>
<p>What the system does, who uses it, and why it exists.</p>
</dd>
<dt class="hdlist1"><strong>Code</strong></dt>
<dd>
<p>Structure, modules, and dependencies.</p>
</dd>
<dt class="hdlist1"><strong>Data</strong></dt>
<dd>
<p>Schemas, golden input/output samples, and data categories.</p>
</dd>
<dt class="hdlist1"><strong>Business</strong></dt>
<dd>
<p>Domain glossary, business rules, and workflows.</p>
</dd>
<dt class="hdlist1"><strong>Architecture</strong></dt>
<dd>
<p>Target stack, coding standards, and non-functional requirements.</p>
</dd>
<dt class="hdlist1"><strong>Test</strong></dt>
<dd>
<p>Equivalence test patterns and behaviors to preserve.</p>
</dd>
<dt class="hdlist1"><strong>Decision</strong></dt>
<dd>
<p>Architecture decision records (ADRs), constraints, and non-negotiables.</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>Each AI task receives a task-specific brief assembled from the relevant slices. Translating a module? The agent gets the legacy code, the business rules, the target architecture, the equivalence tests, and an example. Writing a migration script? It gets schemas, transformation rules, and golden datasets for verification.</p>
</div>
<div class="paragraph">
<p>Targeted context produces precise output. Generic context produces generic output.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_spec_driven_development">Spec-driven development</h2>
<div class="sectionbody">
<div class="paragraph">
<p>For regulated domains&#8201;&#8212;&#8201;banking, insurance, health&#8201;&#8212;&#8201;we take this a step further with spec-driven development. Between the reverse-engineering and rebuild phases, business rules and integration contracts are turned into executable specifications: behavior scenarios (Given/When/Then), contract tests for every API boundary, and property-based tests for edge cases.</p>
</div>
<div class="paragraph">
<p>These specs become the acceptance criteria that AI agents use as guardrails, auditors use to verify compliance, and CI pipelines use to catch regressions immediately. The specification becomes the single source of truth for what the system must do.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_why_this_should_excite_engineers_not_frighten_them">Why this should excite engineers, not frighten them</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A common reaction to these changes is anxiety: "If AI writes the code, what am I for?"</p>
</div>
<div class="paragraph">
<p>The answer is: everything that matters. Judgment, domain understanding, architecture decisions, verification, quality&#8201;&#8212;&#8201;these are the hard parts of software engineering. They always have been. Writing the code was never the bottleneck; understanding what to write and why was.</p>
</div>
<div class="paragraph">
<p>Engineers who embrace orchestration&#8201;&#8212;&#8201;who learn to prepare context, structure tasks for AI agents, and verify output rigorously&#8201;&#8212;&#8201;will find themselves dramatically more productive. A single engineer with well-curated context and good methodology can now deliver what previously required a team of five.</p>
</div>
<div class="paragraph">
<p>But engineers who insist on typing every line themselves, who resist the shift from instrumentalist to conductor, will find themselves increasingly unable to match the output of their AI-augmented peers. Being a good engineer is not about knowing a particular technology. It is about understanding the business and learning fast.</p>
</div>
<div class="paragraph">
<p>The question is not whether this transformation is coming. It is already here. The question is whether you will lead it or be overtaken by it.</p>
</div>
<hr>
<div class="paragraph">
<p><em>This is the fourth in a series of five articles based on the talk "Software Will Cost Almost Nothing. What Happens Next?" In the final article, we will reveal the complete methodology that makes AI-accelerated legacy modernization reliable and reproducible.</em></p>
</div>
<div class="paragraph">
<p><em>Contact: <a href="mailto:nicolas.leroux@lunatech.com">nicolas.leroux@lunatech.com</a> | <a href="https://lunatech.com">lunatech.com</a></em></p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>AI Is Collapsing the Cost of Software. Here Are the Numbers.</title>
		<link>https://blog.lunatech.com//posts/2026-05-12-ai-is-collapsing-the-cost-of-software</link>
		
		<dc:creator><![CDATA[Nicolas Leroux]]></dc:creator>
        <pubDate>2026-05-12T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[AI]]></category>
                }
             {
            <category><![CDATA[software cost]]></category>
                }
             {
            <category><![CDATA[agentic coding]]></category>
                }
             {
            <category><![CDATA[productivity]]></category>
                }
             {
            <category><![CDATA[legacy modernization]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-05-12-ai-is-collapsing-the-cost-of-software</guid>

					<description>
                        <![CDATA[ Every previous industrial revolution replaced muscle. Steam engines, assembly lines, tractors -- each one amplified physical labor and made goods cheaper to produce. The revolution happening right now is different in kind. For the first time, what is being replaced is the ability to produce structured intellectual output: code, documentation, test plans, architecture decisions, migration scripts, compliance reports.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Every previous industrial revolution replaced muscle. Steam engines, assembly lines, tractors&#8201;&#8212;&#8201;each one amplified physical labor and made goods cheaper to produce. The revolution happening right now is different in kind. For the first time, what is being replaced is the ability to produce structured intellectual output: code, documentation, test plans, architecture decisions, migration scripts, compliance reports.</p>
</div>
<div class="paragraph">
<p>This is not a future scenario. It is already measurable, benchmarked, and accelerating.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_timeline_of_ai_assisted_development">The timeline of AI-assisted development</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The shift has happened in barely five years. In 2020, AI in software development meant autocomplete&#8201;&#8212;&#8201;Copilot-style code suggestions that saved a few keystrokes. By 2022, ChatGPT had replaced Stack Overflow as the first place developers looked for answers. In 2024, AI became embedded in CI/CD pipelines: generating tests, reviewing pull requests, checking architecture consistency. And in 2026, we have entered the era of agentic coding&#8201;&#8212;&#8201;AI agents that plan, implement, and verify multi-step tasks autonomously.</p>
</div>
<div class="paragraph">
<p>Each of these stages represents not just a feature improvement but a qualitative shift in what AI can do. We have gone from "help me write a function" to "here is the specification, the constraints, and the context&#8201;&#8212;&#8201;build the module, write the tests, and verify the output."</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_numbers_do_not_lie">The numbers do not lie</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The benchmarks are clear and consistent across multiple independent studies.</p>
</div>
<div class="paragraph">
<p>46% of code written by GitHub Copilot users is now AI-generated. A study across 4,000 developers showed a 26% productivity boost. Controlled experiments report 55% faster task completion.</p>
</div>
<div class="paragraph">
<p>And these are already outdated figures. GPT-4 in 2023 was good at snippets. Claude 3.5 in 2024 could handle full-file rewrites. Claude Code in 2025 introduced agentic workflows&#8201;&#8212;&#8201;multi-step tasks with self-correction. Claude Opus 4 brought multi-step planning, self-verification, and parallel agent execution.</p>
</div>
<div class="paragraph">
<p>Every six months, a generation leap. The curve is not flattening&#8201;&#8212;&#8201;it is steepening.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-12-ai-is-collapsing-the-cost-of-software/cost-collapse.png" alt="Costs">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_becomes_economically_viable">What becomes economically viable</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The real impact is not that developers type less. It is that entire categories of projects that were previously too expensive suddenly become affordable.</p>
</div>
<div class="paragraph">
<p>Legacy systems that were too expensive to replace? Now within reach. Custom software that was reserved for large budgets? Accessible to smaller companies. Complete test suites that nobody had time to write? Generated automatically.</p>
</div>
<div class="paragraph">
<p>Consider the concrete numbers:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Project</th>
<th class="tableblock halign-left valign-top">2020 (Traditional)</th>
<th class="tableblock halign-left valign-top">2026 (AI-Accelerated)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">COBOL Migration (1M lines)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">$9.1M, 18 months</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">$2-4M, 6-8 months</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom CRM Development</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">$500K, 12 months</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">$80K, 3 months</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Full Test Suite (legacy app)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">$200K, 6 months</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">$15K, 2 weeks</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Same scope. Same quality. Fraction of the cost.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_why_this_matters_for_legacy_modernization">Why this matters for legacy modernization</h2>
<div class="sectionbody">
<div class="paragraph">
<p>For decades, the standard answer to "should we modernize?" has been a cost-benefit analysis where the costs were terrifyingly high and the benefits were uncertain. That calculus has fundamentally changed.</p>
</div>
<div class="paragraph">
<p>The average cost of modernization&#8201;&#8212;&#8201;$9.1 million per Deloitte&#8201;&#8212;&#8201;was the perfect excuse to do nothing. But when AI can reverse-engineer business rules from a COBOL codebase in two weeks instead of three months, when it can translate modules with verified behavioral equivalence, when it can generate test suites from golden datasets automatically, the cost curve collapses.</p>
</div>
<div class="paragraph">
<p>This does not mean modernization becomes trivial. It means it becomes feasible. The economic barrier that protected legacy systems from replacement has eroded. What remains is the organizational will to act.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_inaction_tax_is_now_unbearable">The inaction tax is now unbearable</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When action cost millions and took years, inaction could be rationalized. The system works. The risk is too high. We will revisit next quarter.</p>
</div>
<div class="paragraph">
<p>But when action costs a fraction of what it did, and the alternative is continuing to pay $200K per year just to keep the lights on, the calculation flips. The "inaction tax"&#8201;&#8212;&#8201;the cumulative cost of maintaining, patching, and working around a legacy system&#8201;&#8212;&#8201;now exceeds the cost of replacing it.</p>
</div>
<div class="paragraph">
<p>When action costs almost nothing, inaction becomes inexcusable.</p>
</div>
<div class="paragraph">
<p>In the next article, we will explore what this means for the people who build software. If AI is producing the code, what is the new role of the engineer?</p>
</div>
<hr>
<div class="paragraph">
<p><em>This is the third in a series of five articles based on the talk "Software Will Cost Almost Nothing. What Happens Next?" Read the full series on our blog.</em></p>
</div>
<div class="paragraph">
<p><em>Sources: GitHub Copilot Impact Study (2025), McKinsey Digital Productivity Report (2024), Google DeepMind (2024), Deloitte Legacy Modernization Cost Analysis (2025), Lunatech client data.</em></p>
</div>
<div class="paragraph">
<p><em>Contact: <a href="mailto:nicolas.leroux@lunatech.com">nicolas.leroux@lunatech.com</a> | <a href="https://lunatech.com">lunatech.com</a></em></p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>The Cost of Inaction: Why Standing Still Is the Most Expensive Decision in Tech</title>
		<link>https://blog.lunatech.com//posts/2026-05-05-the-cost-of-inaction</link>
		
		<dc:creator><![CDATA[Nicolas Leroux]]></dc:creator>
        <pubDate>2026-05-07T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[cost of inaction]]></category>
                }
             {
            <category><![CDATA[legacy modernization]]></category>
                }
             {
            <category><![CDATA[tech debt]]></category>
                }
             {
            <category><![CDATA[digital transformation]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-05-05-the-cost-of-inaction</guid>

					<description>
                        <![CDATA[ In 1977, Ken Olsen, the founder of Digital Equipment Corporation, declared: "There is no reason anyone would want a computer in their home." DEC was worth $14 billion at the time. It no longer exists.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In 1977, Ken Olsen, the founder of Digital Equipment Corporation, declared: "There is no reason anyone would want a computer in their home." DEC was worth $14 billion at the time. It no longer exists.</p>
</div>
<div class="paragraph">
<p>This is not a story about prediction failures. It is a story about what happens when organizations see disruption coming and choose to stand still. And if you are running a legacy system in 2026, this story is about you.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_famous_last_words">Famous last words</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The pattern is always the same. A dominant player sees the future but decides that the present is comfortable enough. Kodak invented the digital camera in 1975&#8201;&#8212;&#8201;thirty-seven years before filing for bankruptcy. Blockbuster declined a partnership with Netflix in 2000 for $50 million; Netflix is now worth over $250 billion. BlackBerry dismissed the iPhone as a toy in 2007 and watched its market share fall to zero.</p>
</div>
<div class="paragraph">
<p>None of these companies lacked engineers, resources, or market intelligence. What they lacked was the willingness to cannibalize their own success in order to survive. They chose the comfort of the status quo over the discomfort of transformation.</p>
</div>
<div class="paragraph">
<p>And the status quo killed them.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_meanwhile_in_2026">Meanwhile, in 2026</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The numbers tell a sobering story. There are still 240 billion lines of COBOL in production worldwide. 43% of banking and insurance systems run on mainframes designed in the 1960s. The average cost to modernize a legacy system has historically been around $9.1 million&#8201;&#8212;&#8201;a figure that has served as the perfect excuse to postpone.</p>
</div>
<div class="paragraph">
<p>Every year, companies spend billions maintaining systems that were obsolete a decade ago: patching vulnerabilities in frameworks that are no longer supported, onboarding developers into codebases that nobody fully understands, and running parallel infrastructure because the old system "still works" even though it cannot integrate with modern tools.</p>
</div>
<div class="paragraph">
<p>"We&#8217;ll modernize next year," says every CTO since 2015. Spoiler: next year never comes. And the bill keeps growing.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_three_hidden_costs">The three hidden costs</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The cost of inaction is rarely visible on a balance sheet. It manifests in three dimensions.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-05-05-the-cost-of-inaction/three-costs.png" alt="The three hidden costs">
</div>
</div>
<div class="paragraph">
<p>The first is <strong>technical</strong>. Every year of postponement adds another layer of tech debt. Interfaces become more brittle, dependencies more tangled, and the pool of developers who understand the system shrinks. The longer you wait, the harder and more expensive the migration becomes. This is not linear&#8201;&#8212;&#8201;it is exponential.</p>
</div>
<div class="paragraph">
<p>The second is <strong>organizational</strong>. Companies that do not modernize tend to develop rigid, flat structures where no one owns the decision to change. Committees form. Reports are commissioned. Proof-of-concept projects are launched and quietly shelved. The organizational muscle for transformation atrophies.</p>
</div>
<div class="paragraph">
<p>The third is <strong>personal</strong>. For the software engineers working on these systems, inaction means stale skill sets. Still building Struts MVC applications in 2026? Because of inertia? A stale skill set does not land new jobs, and it does not keep old ones interesting. Being a good engineer is not about knowing a particular technology. It is about understanding the business and learning fast.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_europes_particular_challenge">Europe&#8217;s particular challenge</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There is a cultural dimension to this as well. Europe favors stability, consensus, and regulation. The US moves fast, encourages risk-taking, and rewards bold innovation. The result? Europe has missed leadership in cloud, AI, social media, and platform economics.</p>
</div>
<div class="paragraph">
<p>This is not because European engineers are less talented. It is because the institutional incentive in Europe is to avoid failure rather than pursue success. And fear of failure leads directly to missed opportunity. Innovation requires courage, not just competence.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_inaction_is_a_choice">Inaction is a choice</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The instinct to delay is understandable. Modernization is complex, expensive, and risky. But the cost of not modernizing is also complex, expensive, and risky&#8201;&#8212;&#8201;it is just less visible.</p>
</div>
<div class="paragraph">
<p>Every day a legacy system stays in production is a day of compounding cost: maintenance overhead, security exposure, opportunity cost of features that cannot be built, and the slow erosion of competitive position.</p>
</div>
<div class="paragraph">
<p>Inaction does not protect you. It just delays the consequences.</p>
</div>
<div class="paragraph">
<p>The good news? The economics of modernization have changed dramatically. In the next article, we will explore how AI is collapsing the cost of building software&#8201;&#8212;&#8201;and why projects that were unthinkable two years ago are now within reach.</p>
</div>
<hr>
<div class="paragraph">
<p><em>This is the second in a series of five articles based on the talk "Software Will Cost Almost Nothing. What Happens Next?" Read the full series on our blog.</em></p>
</div>
<div class="paragraph">
<p><em>Contact: <a href="mailto:nicolas.leroux@lunatech.com">nicolas.leroux@lunatech.com</a> | <a href="https://lunatech.com">lunatech.com</a></em></p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Software Will Cost Almost Nothing. What Happens Next?</title>
		<link>https://blog.lunatech.com//posts/2026-04-28-software-will-cost-almost-nothing</link>
		
		<dc:creator><![CDATA[Nicolas Leroux]]></dc:creator>
        <pubDate>2026-04-28T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[AI]]></category>
                }
             {
            <category><![CDATA[software engineering]]></category>
                }
             {
            <category><![CDATA[legacy modernization]]></category>
                }
             {
            <category><![CDATA[agentic coding]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-04-28-software-will-cost-almost-nothing</guid>

					<description>
                        <![CDATA[ Every industrial revolution until now replaced muscle. The steam engine, the assembly line, the tractor -- each one amplified physical labor and made goods cheaper to produce. But the revolution we are living through right now is fundamentally different. For the first time, what is being replaced is the ability to produce structured intellectual output: code, documentation, test plans, architecture decisions, migration scripts, compliance reports. All of these can now be produced by AI agents.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Every industrial revolution until now replaced muscle. The steam engine, the assembly line, the tractor&#8201;&#8212;&#8201;each one amplified physical labor and made goods cheaper to produce. But the revolution we are living through right now is fundamentally different. For the first time, what is being replaced is the ability to produce structured intellectual output: code, documentation, test plans, architecture decisions, migration scripts, compliance reports. All of these can now be produced by AI agents.</p>
</div>
<div class="paragraph">
<p>The implications for our industry are staggering. If software&#8201;&#8212;&#8201;the most valuable and most expensive asset of the modern enterprise&#8201;&#8212;&#8201;can be produced at a fraction of today&#8217;s cost, then what changes?</p>
</div>
<div class="paragraph">
<p>Everything.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_cost_of_inaction_is_no_longer_abstract">The cost of inaction is no longer abstract</h2>
<div class="sectionbody">
<div class="paragraph">
<p>At RivieraDev 2025, my co-speaker Quentin Adam and I delivered a keynote titled "The Cost of Inaction." The thesis was simple: standing still is the most expensive decision a company can make.</p>
</div>
<div class="paragraph">
<p>The evidence was everywhere. Kodak invented the digital camera in 1975 and filed for bankruptcy in 2012. Blockbuster declined a partnership with Netflix in 2000 and closed 9,000 stores. BlackBerry dismissed the iPhone in 2007 and holds zero market share today. None of these companies lacked talent or technology. They lacked the courage to act.</p>
</div>
<div class="paragraph">
<p>Meanwhile, in 2026, 240 billion lines of COBOL remain in production. 43% of banking and insurance systems still run on mainframes. The average cost to modernize? $9.1 million&#8201;&#8212;&#8201;until now.</p>
</div>
<div class="paragraph">
<p>The hidden costs of inaction are technical (accumulated tech debt, lost innovation capacity), organizational (flat structures that lack ownership), and personal (stalled careers, missed opportunities). Inaction does not protect you. It just delays the consequences.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_but_what_if_the_cost_of_action_collapses">But what if the cost of action collapses?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This is where the story takes a dramatic turn. AI-assisted software development has moved from autocomplete in 2020 to fully agentic coding in 2026. The numbers are unambiguous: 46% of code written by Copilot users is AI-generated, productivity gains of 26% across thousands of developers, and 55% faster task completion in controlled experiments. And these benchmarks are already months old&#8201;&#8212;&#8201;every quarter brings another generation leap.</p>
</div>
<div class="paragraph">
<p>Consider what this means in practical terms. A COBOL migration that cost $9.1 million and took 18 months in 2020 can now be completed for $2-4 million in 6-8 months. A custom CRM that required $500K and a year of development can be built for $80K in three months. A full test suite for a legacy application that would have cost $200K and six months of manual work can be generated for $15K in two weeks.</p>
</div>
<div class="paragraph">
<p>Same scope. Same quality. Fraction of the cost.</p>
</div>
<div class="paragraph">
<p>When action costs almost nothing, inaction becomes inexcusable.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_engineers_become_orchestrators">Engineers become orchestrators</h2>
<div class="sectionbody">
<div class="paragraph">
<p>But there is a catch. AI does not mean pressing a button and watching the magic happen. Without structure, AI produces plausible but generic output that needs extensive rework. The difference between success and failure lies entirely in methodology.</p>
</div>
<div class="paragraph">
<p>The new role of the software engineer is not to type code. It is to orchestrate AI agents&#8201;&#8212;&#8201;curating context, defining quality gates, structuring work, verifying output, and accumulating knowledge over time. Like a conductor in front of an orchestra, the engineer sets the tempo, the dynamics, and the interpretation. The performance depends entirely on the score.</p>
</div>
<div class="paragraph">
<p>This is captured in a simple equation:</p>
</div>
<div class="paragraph lead">
<p><strong>AI Output Quality = AI Capability x Context Quality</strong></p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2026-04-28-software-will-cost-almost-nothing/equation.png" alt="The AI Equation: Output Quality = AI Capability × Context Quality">
</div>
</div>
<div class="paragraph">
<p>AI capability improves every quarter. You cannot control it. Context quality is the lever you own. This is where engineering discipline makes the difference between a demo and production-grade software.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_a_battle_tested_methodology">A battle-tested methodology</h2>
<div class="sectionbody">
<div class="paragraph">
<p>At Lunatech, we have spent the past year building a methodology that makes AI-accelerated modernization reliable, reproducible, and predictable. Our Legacy Modernization with AI framework is built on seven phases&#8201;&#8212;&#8201;Discover, Reverse-Engineer, Re-Architect, Rebuild, Migrate, Harden, Handover&#8201;&#8212;&#8201;each with clear deliverables and exit gates.</p>
</div>
<div class="paragraph">
<p>At the heart of the framework is the Claude (or Copilot) Context Pack: a versioned, curated knowledge base with seven layers (System, Code, Data, Business, Architecture, Test, Decision) that feeds every AI task with precisely the right context. A dedicated AI engineer builds, curates, and maintains it throughout the engagement.</p>
</div>
<div class="paragraph">
<p>The result? Small teams of 2-4 senior engineers plus a dedicated AI engineer consistently outperform larger traditional teams. Parity is verified through behavioral equivalence testing and parallel running&#8201;&#8212;&#8201;not assumed. And the context pack is transferred to the client as a durable asset at handover: the living documentation the legacy system never had.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_this_means_for_you">What this means for you</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you are a CTO sitting on a legacy system and thinking "we&#8217;ll modernize next year," know that next year never comes. And the bill keeps growing. If you are a software engineer still building the same kind of applications with the same kind of tools, your skill set is becoming stale. The competitive advantage is shifting from code production to speed and judgment.</p>
</div>
<div class="paragraph">
<p>The cost of software is collapsing. The skill is no longer writing code&#8201;&#8212;&#8201;it is orchestrating agents. And methodology is what separates production-grade output from demos.</p>
</div>
<div class="paragraph">
<p>Take the first step. Start something.</p>
</div>
<hr>
<div class="paragraph">
<p><em>This is the first in a series of five articles based on my talk "Software Will Cost Almost Nothing. What Happens Next?" For more details on each theme, follow the series on our blog and on LinkedIn.</em></p>
</div>
<div class="paragraph">
<p><em>Contact: <a href="mailto:nicolas.leroux@lunatech.com">nicolas.leroux@lunatech.com</a> | <a href="https://lunatech.com">lunatech.com</a></em></p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Devoxx France 2026</title>
		<link>https://blog.lunatech.com//posts/2026-04-24-devoxx-france-2026</link>
		
		<dc:creator><![CDATA[Antoine Bastos]]></dc:creator>
        <pubDate>2026-04-24T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[devoxx-fr]]></category>
                }
             {
            <category><![CDATA[ai]]></category>
                }
             {
            <category><![CDATA[java]]></category>
                }
             {
            <category><![CDATA[conference]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2026-04-24-devoxx-france-2026</guid>

					<description>
                        <![CDATA[ I spent two days at https://www.devoxx.fr/[Devoxx France 2026] at the Palais des Congrès in Paris. The buzz words of this edition were hard to miss: AI, Vibe Coding, Claude, Agentic, Context, Determinism. Out of sixteen talks I attended, more than half dealt with AI in some form. The conference carried genuine optimism : barriers are falling, tools are powerful, now is the time to experiment. But the talks that stuck with me most went further. ]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>I spent two days at <a href="https://www.devoxx.fr/">Devoxx France 2026</a> at the Palais des Congrès in Paris. The buzz words of this edition were hard to miss: AI, Vibe Coding, Claude, Agentic, Context, Determinism. Out of sixteen talks I attended, more than half dealt with AI in some form. The conference carried genuine optimism : barriers are falling, tools are powerful, now is the time to experiment. But the talks that stuck with me most went further.
They examined where AI breaks, what it costs, and what discipline it demands. Here is what I took away.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_ai_will_solve_everything_right">AI Will Solve Everything&#8230;&#8203; Right?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Devoxx opened with keynotes spanning from enthusiasm to existential warning.</p>
</div>
<div class="sect2">
<h3 id="_prompt_ship_test">Prompt, Ship, Test</h3>
<div class="paragraph">
<p>Nicolas Grenié set the tone on day one. The barriers to building are collapsing. Solo developers ship prototypes that would have taken teams months. Product Managers pick up Figma and Claude and start dreaming. His message: experiment now, ship, test. Both the tools and the way we use them will keep changing. Do not wait.</p>
</div>
</div>
<div class="sect2">
<h3 id="_the_right_to_be_lazy">The Right to Be Lazy</h3>
<div class="paragraph">
<p>Jean-Gabriel Ganascia, a philosopher and engineer, delivered his entire keynote without a single slide, a rare move at Devoxx. He opened with Paul Lafargue&#8217;s 19th-century manifesto <em>The Right to Be Lazy</em>: if machines handle the drudgery, humans should work three hours a day and spend the rest thinking.</p>
</div>
<div class="paragraph">
<p>Generative AI seems to fulfill that promise. It reads thousands of pages of reports, produces them, makes decisions. We keep only the rewarding tasks. "From that perspective, it&#8217;s wonderful," Ganascia said.</p>
</div>
<div class="paragraph">
<p>Then he flipped the argument and laid out four risks:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Boredom.</strong> If we delegate cognitive work, will we have enough to keep our minds occupied? He quoted Baudelaire&#8217;s <em>Fleurs du mal</em>, "It is Boredom!", as a warning.</p>
</li>
<li>
<p><strong>The capitalist hydra won&#8217;t wither away.</strong> "Be honest — do you really believe that? Not long ago Meta laid off 8,000 people." AI will make large companies more profitable, not free workers.</p>
</li>
<li>
<p><strong>De-skilling.</strong> If we stop learning to read, write, and code on our own, what remains?</p>
</li>
<li>
<p><strong>Moral alignment as censorship.</strong> Who decides what an AI may or may not say? Ganascia mentioned France Travail&#8217;s plan for AI-assisted annual interviews, a project that raises questions about large-scale behavioral control.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>His closing warning: after the Bronze Age, the Iron Age, and the Information Age, we risk entering the Age of Falsification and Control.</p>
</div>
</div>
<div class="sect2">
<h3 id="_vibe_coding_meets_reality">Vibe Coding Meets Reality</h3>
<div class="paragraph">
<p>Marjory Canonne, who advises SMEs on AI adoption, described the gap between expectations and outcomes. Many companies approach AI in FOMO mode, hoping for immediate ROI. When she explains what AI actually is and what it costs, many lose interest.</p>
</div>
<div class="paragraph">
<p>Her advice to companies: start by systematizing internal knowledge. Mine your data, capitalize on experience, speed up information access across the organization. That delivers value. Jumping straight to customer-facing AI products does not. During Q&amp;A, a developer asked: what do you do when a Product Manager vibe-codes a prototype, then says "we can cut your budget, one dev will clean this up"? She responded that some companies will have to learn that the hard way. Grenié added with humour: "Where we used to have 500 days to deliver, we&#8217;ll now have 5 days of vibe-coded prototype and 495 days cleaning up the mess."</p>
</div>
<div class="paragraph">
<p>My own concern here: there is no certainty about the future of LLMs. Today AI is cheap. Providers sell at a loss. Putting an AI-as-a-service (GPT, Claude, Gemini) at the core of a product&#8217;s business domain means accepting uncertainty by design: non-deterministic behavior, provider version changes, price hikes, or disappearance.</p>
</div>
</div>
<div class="sect2">
<h3 id="_datacenters_and_the_sovereignty_paradox">Datacenters and the Sovereignty Paradox</h3>
<div class="paragraph">
<p>Loup Cellard, an anthropology researcher at Sciences Po, shifted the conversation to physical infrastructure. His talk mapped how submarine cables shape datacenter deployments in France. Marseille (7th best-connected city in the world) and Bordeaux (where Meta&#8217;s "Amitié" cable arrives) concentrate the installations. The result: conflicts over energy between public transit and datacenters, land speculation, and a sovereignty paradox. Local authorities told Cellard they do not understand the government&#8217;s position: France talks about digital sovereignty while welcoming US players, sometimes on publicly funded infrastructure.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_engineering_with_ai_agents_discipline_over_hype">Engineering with AI Agents: Discipline Over Hype</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If the keynotes asked "should we?", the afternoon talks asked "how do we do it properly?"</p>
</div>
<div class="sect2">
<h3 id="_four_patterns_for_agentic_ai">Four Patterns for Agentic AI</h3>
<div class="paragraph">
<p>Guillaume Laforge presented four design patterns for AI agents, each addressing the same core problem: LLMs degrade when you give them too much at once.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Progressive Disclosure.</strong> Organize agent capabilities into skills, each with an abstract header and a detailed body. A <code>SKILL.md</code> index lets the agent load only what it needs. (See <a href="https://github.com/glaforge/deslopify">Deslopify skills</a> on GitHub.)</p>
</li>
<li>
<p><strong>Hierarchical Decomposition.</strong> Break work into sequential steps with specialized agents. One writes an article, another de-slops it. The tradeoff is more latency and sometimes more tokens.</p>
</li>
<li>
<p><strong>LLM-as-Judge.</strong> Use one model to evaluate another&#8217;s output. Three summaries from Gemini 2.5 Flash Lite plus one review from Gemini 2.5 Flash costs roughly $0.80, versus $2.00 for a single Gemini 3.1 Pro summary, and produces better results.</p>
</li>
<li>
<p><strong>GOAP (Goal-Oriented Action Planning).</strong> A supervisor LLM receives a high-level objective and orchestrates specialized agents. Laforge demonstrated this with LangChain4j.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_constrain_the_puzzle">Constrain the Puzzle</h3>
<div class="paragraph">
<p>Cyrille Martraire and Olivier Penhoat presented a legacy migration case study where they used AI to generate end-to-end API tests.</p>
</div>
<div class="paragraph">
<p>Their first approach was to have the AI run the tests by querying both APIs and comparing responses. It worked 100% of the time, but was slow, expensive, and non-deterministic. JSON payloads of ~400 KB ate the context window, and results varied between runs.</p>
</div>
<div class="paragraph">
<p>Their second approach worked: the AI generates the tests instead of running them. They constrained the problem with two files: a <code>.yml</code> describing test scenarios and a <code>pivot-format.spec.md</code> specifying how to map old API responses to new ones. The AI fills in only the test implementation. Expensive once (generation), nearly free afterwards (standard execution). The principle: <strong>constrain the AI to fill one piece of the puzzle, not generate the whole puzzle.</strong> The more precise the framework you provide, the more deterministic the output.</p>
</div>
</div>
<div class="sect2">
<h3 id="_context_engineering_for_deterministic_ai">Context Engineering for Deterministic AI</h3>
<div class="paragraph">
<p>Benoît Fontaine&#8217;s talk on taming AI agents reinforced the same idea from the tooling side. His key points:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Performance degrades not because of context size, but because of what is in the context. Compact at 60%, never exceed 80%. If auto-compaction kicks in, the session is dead.</p>
</li>
<li>
<p>Maintain at least two files: <code>AGENTS.md</code> (cross-tool source of truth with architecture rules, conventions, workflows, strict rules) and <code>CLAUDE.md</code> (tool-specific adapter). Keep each under 200 lines.</p>
</li>
<li>
<p>Place scoped context files in relevant directories (<code>/frontend</code>, <code>/backend</code>).</p>
</li>
<li>
<p>Ideal session workflow: research, compact, plan, compact, implement.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>His closing line resonated: "AI is an amplifier of the current state. If the documentation and specs were already clean, the AI output will be clean." In other words, operational maturity matters more than which model you pick.</p>
</div>
<div class="paragraph">
<p>Presentation repo: <a href="https://github.com/b-fontaine/devoxx-2026" class="bare">https://github.com/b-fontaine/devoxx-2026</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_claude_code_in_30_minutes">Claude Code in 30 Minutes</h3>
<div class="paragraph">
<p>Erwan Gereec delivered 30 Claude Code tips in 30 minutes. A few stood out:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>/init</code> analyzes the codebase and initializes <code>CLAUDE.md</code>, a natural starting point for context engineering.</p>
</li>
<li>
<p><code>/compact keep the developed API and remove explorations</code> targets compaction instead of blind compression.</p>
</li>
<li>
<p><code>/insights</code> generates a detailed HTML report analyzing your usage habits, with personalized tips.</p>
</li>
<li>
<p>Custom skills live in <code>.claude/skills/&lt;name&gt;/SKILL.md</code> for repeatable workflows.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_keeps_shipping">Java Keeps Shipping</h2>
<div class="sectionbody">
<div class="paragraph">
<p>AI dominated, but Java had its own strong showing.</p>
</div>
<div class="sect2">
<h3 id="_value_types_codes_like_a_class_works_like_an_int">Value Types: Codes Like a Class, Works Like an Int</h3>
<div class="paragraph">
<p>If you hadn&#8217;t already followed java incoming evolutions, Rémi Forax and Clément de Tastes presented <a href="https://openjdk.org/jeps/401">Project Valhalla</a>, the largest refactoring in JDK history (2,665 files modified, +200k lines). The core idea : using a new <code>value</code> keyword before <code>class</code> or <code>record</code> gives up the object&#8217;s identity in exchange for memory flattening and scalarization.</p>
</div>
<div class="paragraph">
<p>Using a <a href="https://github.com/rokon12/Mandelbrot">Mandelbrot</a> visualisation tool, they showed that switching from <code>record</code> to <code>value record</code> reduced GC pressure by 143x and memory usage by 88%. The JIT compiler can scalarize value types, copying field values directly into CPU registers because the objects are immutable and identity-free.</p>
</div>
<div class="paragraph">
<p>Beyond raw performance, this will inevitably change the way we code by using more immutable objects and data structures. We must be aware now that current JDK <code>@ValueBased</code> annotated classes will automatically become value classes (no recompilation needed for old libraries).
Furthermore, a new <code>!</code> (bang) operator will guarantee non-nullity for array flattening, and <code>==</code> comparison will be possible on value objects (in that case, this is not a reference comparison, but a value comparison). But there are traps though : <code>==</code> on a value class holding a <code>String</code> field compares the reference, not the content. Moreover, <code>synchronized</code> won&#8217;t work because the lock relies on the object header, which won&#8217;t exist on value classes.</p>
</div>
</div>
<div class="sect2">
<h3 id="_structured_concurrency">Structured Concurrency</h3>
<div class="paragraph">
<p>José Paumard ran a 3-hour hands-on lab on the Structured Concurrency API (Project Loom), available in JDK 27 early access. Virtual threads (JDK 21) made threads cheap to create, but Java still lacked a concurrency model to match. Running parallel tasks meant either chaining <code>CompletableFuture</code> callbacks or adopting a reactive framework — both sacrifice readability and stack traces. Structured Concurrency fills that gap: fork tasks on virtual threads, write sequential-looking code, and let the scope handle cancellation and error propagation automatically.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">try (var scope = StructuredTaskScope.open(Joiner.allSuccessfulOrThrow())) {
    scope.fork(taskA);
    scope.fork(taskB);
    scope.join();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Multiple <code>Joiner</code> strategies handle success, failure, and timeouts. Structured Concurrency isn’t new as an idea. What Project Loom brings is making it finally practical at scale thanks to virtual threads, because platform threads are expensive to create and do not scale well when used in large numbers.</p>
</div>
<div class="paragraph">
<p>Lab repo: <a href="http://github.com/JosePaumard/2026_DevoxxFR-Loom-lab" class="bare">http://github.com/JosePaumard/2026_DevoxxFR-Loom-lab</a></p>
</div>
</div>
<div class="sect2">
<h3 id="_in_brief">In Brief</h3>
<div class="ulist">
<ul>
<li>
<p><strong>Victor Rentea</strong> delivered the funniest talk of the conference on Event-Driven Architecture pitfalls, covering ordering, partition keys, phantom writes, and idempotency. It moved too fast to capture in notes. Worth rewatching on <a href="https://youtube.com/@devoxxfrvideos?si=fo6T_UhA31Q1lx31">YouTube</a>.</p>
</li>
<li>
<p><strong>Thomas Pierrain and Julien Topçu</strong> presented "The Hive." Starting from a Big Ball of Mud, they showed how to modularize a monolith through vertical slicing, then isolate each module as a hexagon with adapters that translate types between modules so domain models never leak from one hexagon to another. Once that structure is in place, extracting a module into a microservice becomes straightforward.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Build up AI skills now. The tools are powerful and priced below cost; that window will close. But tooling alone won&#8217;t save you, as clean documentation and clear specs make AI output better, while sloppy inputs produce sloppy outputs, faster.
AI needs a clear framework to fill in the missing pieces; constraining the puzzle is the key to tackle its lack of determinism.
And Java keeps evolving. Value types, structured concurrency, and virtual threads address real performance and concurrency pain points. Valhalla alone justifies paying attention to the next JDK releases.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Change Detection Strategies in Angular: Zones and Signals</title>
		<link>https://blog.lunatech.com//posts/2025-11-05-change-detection-strategies-in-angular-zones-and-signals</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-11-05T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[Intermediate]]></category>
                }
             {
            <category><![CDATA[change detection]]></category>
                }
             {
            <category><![CDATA[Zone.js]]></category>
                }
             {
            <category><![CDATA[OnPush]]></category>
                }
             {
            <category><![CDATA[signals (intro)]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-11-05-change-detection-strategies-in-angular-zones-and-signals</guid>

					<description>
                        <![CDATA[ Understand Angular’s change detection mechanism and how to optimize performance using the OnPush strategy. Learn how to use Signals for more granular reactivity. This article provides a practical mental model and shows how to apply it with Zone.js, OnPush, and Signals, along with a clear path toward building zoneless applications when you’re ready.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Understand Angular’s change detection mechanism and how to optimize performance using the OnPush strategy. Learn how to use Signals for more granular reactivity. This article provides a practical mental model and shows how to apply it with Zone.js, OnPush, and Signals, along with a clear path toward building zoneless applications when you’re ready.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_mental_model_how_angular_knows_to_update">The mental model: how Angular knows to update</h3>
<div class="paragraph">
<p>At a high level, Angular renders your component tree into a graph of views. A change detection pass checks template bindings and updates the DOM where something changed. Something has to tell Angular when to do that pass:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>With Zone.js (the default), Angular patches async APIs (Promises, setTimeout, DOM events, etc.) and schedules checks automatically after those tasks.</p>
</li>
<li>
<p>With signals, writes to a signal schedule the right components to re-render even without Zone.js.</p>
</li>
<li>
<p>With OnPush, Angular narrows when a component is checked: on input reference changes, events in that component, async pipe emissions, and signal writes.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Once you see what triggers checks, you can guide Angular to do less work and update only when it matters.</p>
</div>
</div>
<div class="sect2">
<h3 id="_working_with_zone_js_default">Working with Zone.js (default)</h3>
<div class="paragraph">
<p>Zone.js intercepts most async boundaries and calls into Angular to run change detection. It’s a safe default and reduces boilerplate, especially for teams migrating legacy code.</p>
</div>
<div class="paragraph">
<p>You can configure Zone.js coalescing to reduce redundant checks:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// app.config.ts
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    // Collapses multiple events/microtasks into fewer change detection cycles.
    provideZoneChangeDetection({
      eventCoalescing: true,
      runCoalescing: true,
    }),
    provideHttpClient(),
  ],
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>For performance-sensitive work (e.g., animations or polling), run it outside Angular to avoid thrashing, and re-enter only when UI state actually changes.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import {
  Component,
  NgZone,
  ChangeDetectionStrategy,
  signal,
  inject,
  OnInit,
  OnDestroy
} from '@angular/core';

@Component({
  selector: 'cpu-heavy',
  standalone: true,
  template: `
    &lt;p&gt;Frames: {{ frames() }}&lt;/p&gt;
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CpuHeavyComponent implements OnInit, OnDestroy {
  private zone = inject(NgZone);

  // Signal handles reactivity; no CDR needed
  private _frames = signal(0);
  frames = this._frames.asReadonly();

  private intervalId?: ReturnType&lt;typeof setInterval&gt;;

  ngOnInit() {
    this.zone.runOutsideAngular(() =&gt; {
      let n = 0;
      this.intervalId = setInterval(() =&gt; {
        // Update signal outside Angular zone
        // Signals batch updates automatically
        this._frames.set(++n);

        if (n &gt; 3000) {
          clearInterval(this.intervalId);
        }
      }, 16);
    });
  }

  ngOnDestroy() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
    }
  }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_embracing_onpush_for_predictable_performance">Embracing OnPush for predictable performance</h3>
<div class="paragraph">
<p>OnPush reduces unnecessary checks and nudges you toward immutable data and explicit updates. Angular will check an OnPush component when:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>An Input reference changes (immutability shines here).</p>
</li>
<li>
<p>The component fires an event or an async pipe emits.</p>
</li>
<li>
<p>A bound signal writes (signals integrate with the scheduler).</p>
</li>
<li>
<p>You explicitly call markForCheck or detectChanges.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Use immutable updates for inputs and track nodes for stable lists.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component, ChangeDetectionStrategy, input } from '@angular/core';

type User = { id: number; name: string };

@Component({
  selector: 'user-list',
  standalone: true,
  template: `
    &lt;ul&gt;
      @for (u of users(); track u.id) {
        &lt;li&gt;{{ u.name }}&lt;/li&gt;
      }
    &lt;/ul&gt;
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserListComponent {
  // Signal-based input (works great with OnPush)
  users = input&lt;ReadonlyArray&lt;User&gt;&gt;([]);
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// parent.component.ts
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { UserListComponent } from './user-list.component';

type User = { id: number; name: string };

@Component({
  selector: 'parent',
  standalone: true,
  imports: [UserListComponent],
  template: `
    &lt;button (click)="add()"&gt;Add&lt;/button&gt;
    &lt;button (click)="mutate()"&gt;Mutate (bad)&lt;/button&gt;

    &lt;user-list [users]="users()"&gt;&lt;/user-list&gt;
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ParentComponent {
  private _users = signal&lt;ReadonlyArray&lt;User&gt;&gt;([
    { id: 1, name: 'Maya' },
    { id: 2, name: 'Dee' },
  ]);
  users = this._users.asReadonly();

  // Correct: immutable update changes the reference and triggers OnPush
  add() {
    const currentIds = this.users().map(u =&gt; u.id);
    const nextId = currentIds.length &gt; 0 ? Math.max(...currentIds) + 1 : 1;
    this._users.update(arr =&gt; [...arr, { id: nextId, name: 'New User' }]);
  }

  // Bad: mutates in place; OnPush children won't see a different reference
  mutate() {
    // as any to showcase the pitfall (avoid in real code)
    (this.users() as any)[0].name = 'Mutated';
    // The UI may not update; prefer immutable patterns.
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you&#8217;re not using signals and you must update UI from a callback that Angular doesn&#8217;t know about, call markForCheck.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit } from '@angular/core';

@Component({
  selector: 'ws-status',
  standalone: true,
  template: `Status: {{ status }}`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WebSocketStatusComponent implements OnInit {
  status = 'disconnected';
  constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit() {
    const ws = new WebSocket('wss://example.org');
    ws.onopen = () =&gt; { this.status = 'connected'; this.cdr.markForCheck(); };
    ws.onclose = () =&gt; { this.status = 'disconnected'; this.cdr.markForCheck(); };
  }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'ws-status',
  standalone: true,
  template: `Status: {{ status }}`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WebSocketStatusComponent {
  status = 'disconnected';
  constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit() {
    const ws = new WebSocket('wss://example.org');
    ws.onopen = () =&gt; { this.status = 'connected'; this.cdr.markForCheck(); };
    ws.onclose = () =&gt; { this.status = 'disconnected'; this.cdr.markForCheck(); };
  }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_signals_intro_finegrained_reactivity_without_heavy_lifting">Signals (intro): fine‑grained reactivity without heavy lifting</h3>
<div class="paragraph">
<p>Signals give you a declarative, dependency-tracked model for state. Reading a signal in a template links it to the view; writing to that signal schedules the right view to update. This makes change detection more targeted and pairs naturally with OnPush or even zoneless.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component, ChangeDetectionStrategy, signal, computed, effect } from '@angular/core';

type Todo = { id: number; title: string; done: boolean };

@Component({
  selector: 'todo-panel',
  standalone: true,
  template: `
    &lt;input placeholder="Filter..." [value]="filter()" (input)="filter.set(($event.target as HTMLInputElement).value)" /&gt;

    &lt;p&gt;Completed: {{ completedCount() }} / {{ todos().length }}&lt;/p&gt;

    &lt;ul&gt;
      @for (t of filteredTodos(); track t.id) {
        &lt;li&gt;
          &lt;label&gt;
            &lt;input type="checkbox" [checked]="t.done" (change)="toggle(t.id)" /&gt;
            {{ t.title }}
          &lt;/label&gt;
        &lt;/li&gt;
      }
    &lt;/ul&gt;
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TodoPanelComponent {
  private _todos = signal&lt;Todo[]&gt;([
    { id: 1, title: 'Ship dashboard', done: false },
    { id: 2, title: 'Write docs', done: true },
  ]);
  todos = this._todos.asReadonly();

  filter = signal('');
  filteredTodos = computed(() =&gt; {
    const q = this.filter().toLowerCase();
    return this.todos().filter(t =&gt; t.title.toLowerCase().includes(q));
  });
  completedCount = computed(() =&gt; this.todos().filter(t =&gt; t.done).length);

  toggle(id: number) {
    this._todos.update(ts =&gt; ts.map(t =&gt; (t.id === id ? { ...t, done: !t.done } : t)));
  }

  // Optional: side effects
  log = effect(() =&gt; {
    console.debug('Todos changed:', this.todos());
  });
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Interop with RxJS is straightforward; toSignal turns an Observable into a signal that drives OnPush views.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { toSignal } from '@angular/core/rxjs-interop';
import { Observable } from 'rxjs';

@Component({ /* ... */ })
export class ClockComponent {
  now = toSignal(
    // Emits a new Date every second
    new Observable&lt;Date&gt;(sub =&gt; {
      const id = setInterval(() =&gt; sub.next(new Date()), 1000);
      return () =&gt; clearInterval(id);
    }),
    { initialValue: new Date() }
  );
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Because writing to signals schedules view updates, you often don’t need ChangeDetectorRef calls. The result is simpler, more predictable change detection.</p>
</div>
</div>
<div class="sect2">
<h3 id="_zoneless_change_detection_when_youre_ready_to_drop_zone_js">Zoneless change detection: when you&#8217;re ready to drop Zone.js</h3>
<div class="paragraph">
<p>Zoneless mode lets Angular skip patching browser APIs (no Zone.js) and rely purely on explicit reactive triggers (signals, template events, async pipe, router). By Angular 20, zoneless is mature enough for production in well-structured, signal‑driven apps.</p>
</div>
<div class="paragraph">
<p>Use the stable provider if it exists; otherwise keep using the experimental one until your project confirms the upgrade path.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// app.config.ts
import { ApplicationConfig, provideHttpClient } from '@angular/common/http';
import {
  // Verify which one exists in your installed version:
  // If Angular 20 exposes `provideZonelessChangeDetection`, prefer it.
  provideZonelessChangeDetection,               // &lt;-- Confirm availability
  provideExperimentalZonelessChangeDetection,   // &lt;-- Fallback if stable not present
} from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    // Use ONE of the following:
    // provideZonelessChangeDetection(),
    provideExperimentalZonelessChangeDetection(), // ← Remove when stable provider confirmed
    provideHttpClient(),
  ],
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>With zoneless, Angular still updates views when:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Template events fire (click, input, etc.).</p>
</li>
<li>
<p>A signal is written (set/update).</p>
</li>
<li>
<p>AsyncPipe emits (it marks its view manually).</p>
</li>
<li>
<p>Router navigation completes.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Other async sources (WebSocket, timers, SDK callbacks): funnel them through signals (or Observables bound via async pipe / toSignal). Avoid manual ChangeDetectorRef unless integrating with legacy patterns.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { interval, map, startWith } from 'rxjs';

@Component({
  selector: 'zoneless-demo',
  standalone: true,
  template: `
    &lt;p&gt;Server time: {{ time() }}&lt;/p&gt;
    &lt;p&gt;Clicks: {{ clicks() }}&lt;/p&gt;
    &lt;button (click)="inc()"&gt;Click me&lt;/button&gt;
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZonelessDemoComponent {
  time = toSignal(
    interval(1000).pipe(
      map(() =&gt; new Date().toLocaleTimeString()),
      startWith(new Date().toLocaleTimeString()),
    ),
    { initialValue: new Date().toLocaleTimeString() },
  );

  private _clicks = signal(0);
  clicks = this._clicks.asReadonly();

  inc() {
    this._clicks.update(n =&gt; n + 1);
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you interact with non-Angular callbacks (e.g., third‑party SDKs), prefer to set signals inside the callback. That write will schedule the needed view updates.</p>
</div>
</div>
<div class="sect2">
<h3 id="_practical_checklist">Practical checklist</h3>
<div class="ulist">
<ul>
<li>
<p>Prefer OnPush for all app components; combine with immutable data.</p>
</li>
<li>
<p>Use signal-based inputs (input()) in leaf components for ergonomic, fine-grained updates.</p>
</li>
<li>
<p>Store local UI state as signals; use computed for derivations and effect for side effects.</p>
</li>
<li>
<p>In Zone.js apps, isolate heavy loops with runOutsideAngular and re-enter only on state writes.</p>
</li>
<li>
<p>In zoneless apps, ensure all UI-affecting async work flows through signals or async pipe.</p>
</li>
<li>
<p>Track list items by a stable key to avoid re-render churn.</p>
</li>
<li>
<p>Use provideZoneChangeDetection with coalescing in legacy/mixed environments; move toward provideExperimentalZonelessChangeDetection when your reactive pathways are solid.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_troubleshooting_gotchas">Troubleshooting gotchas</h3>
<div class="ulist">
<ul>
<li>
<p>“My OnPush child didn’t update.” Ensure the Input reference changes (immutable update) or the child reads a signal that you wrote to.</p>
</li>
<li>
<p>“Zoneless didn’t repaint after a callback.” Convert that callback’s data to a signal or trigger an async pipe emission; avoid manual detectChanges unless you know the implications.</p>
</li>
<li>
<p>“Perf regressions after refactor.” Check for unintended synchronous effects that write many signals in a loop; batch updates if needed, or compose a single state write.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_conclusion">Conclusion</h3>
<div class="paragraph">
<p>Change detection in Angular is powerful because you can choose the right tool for the job. Zone.js gives you simplicity, OnPush gives you predictability and performance, and signals give you precise, maintainable reactivity. Together, they help you ship fast UIs without guesswork, and you can progressively adopt a zoneless model when your state is signal-driven.</p>
</div>
</div>
<div class="sect2">
<h3 id="_next_steps">Next Steps</h3>
<div class="ulist">
<ul>
<li>
<p>Audit your components: enable OnPush and fix any mutable input flows.</p>
</li>
<li>
<p>Introduce signals for local state and derived values; replace ad‑hoc ChangeDetectorRef calls.</p>
</li>
<li>
<p>Wrap recurring async sources (timers, sockets) with toSignal or async pipe.</p>
</li>
<li>
<p>Experiment with provideExperimentalZonelessChangeDetection in a feature slice to validate your reactive pathways.</p>
</li>
<li>
<p>Read the Angular guides on signals and zoneless change detection to deepen your understanding.</p>
</li>
</ul>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Getting Started with Angular 20 CLI and Standalone Components</title>
		<link>https://blog.lunatech.com//posts/2025-10-27-getting-started-with-angular-20-cli-and-standalone-components</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-10-27T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[Beginner]]></category>
                }
             {
            <category><![CDATA[Angular CLI]]></category>
                }
             {
            <category><![CDATA[standalone components]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-10-27-getting-started-with-angular-20-cli-and-standalone-components</guid>

					<description>
                        <![CDATA[ Learn how to initialize and structure a new Angular 20 project using the Angular CLI. Understand standalone components and the simplified bootstrapping process. This guide focuses on a clean project setup, modern routing, and practical patterns you can reuse in real projects.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Learn how to initialize and structure a new Angular 20 project using the Angular CLI. Understand standalone components and the simplified bootstrapping process. This guide focuses on a clean project setup, modern routing, and practical patterns you can reuse in real projects.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_prerequisites">Prerequisites</h3>
<div class="ulist">
<ul>
<li>
<p>Node.js 24+ (LTS recommended) and npm or pnpm</p>
</li>
<li>
<p>Angular CLI 20 installed globally: npm i -g @angular/cli@20</p>
</li>
<li>
<p>Familiarity with TypeScript and Angular fundamentals</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_create_a_new_project_with_the_angular_cli">Create a new project with the Angular CLI</h3>
<div class="paragraph">
<p>The Angular CLI scaffolds a production-grade baseline with sensible defaults. Standalone components are the default in Angular 20, which means no NgModules are required for most apps.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-shell" data-lang="shell">// Terminal
// Create a new app with routing and SCSS. Use --ssr to scaffold server-side rendering.
ng new lt-dashboard --routing --style=scss
cd lt-dashboard
ng serve</code></pre>
</div>
</div>
<div class="paragraph">
<p>Open <a href="http://localhost:4200" class="bare">http://localhost:4200</a> and confirm the app runs. The CLI gives you fast dev server reloads, TypeScript checks, and a ready-to-extend project.</p>
</div>
</div>
<div class="sect2">
<h3 id="_project_structure_that_scales">Project structure that scales</h3>
<div class="paragraph">
<p>A small, predictable layout keeps cognitive load low as features grow.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// Suggested structure (simplified)
src/
  main.ts
  index.html
  styles.scss
  environments/
    environment.ts
    environment.prod.ts
  app/
    app.component.ts
    app.routes.ts
    features/
      home/
        home.component.ts
      projects/
        projects.component.ts</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>app.routes.ts</strong> holds your route config.</p>
</li>
<li>
<p>Each feature gets its own folder with a standalone component.</p>
</li>
<li>
<p>environments holds build-time configuration.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_simplified_bootstrapping_with_bootstrapapplication">Simplified bootstrapping with bootstrapApplication</h3>
<div class="paragraph">
<p>Standalone components and functional providers eliminate boilerplate. The entry point is lean and explicit.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { provideHttpClient, HttpInterceptorFn, withInterceptors } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';

// Example functional interceptor (adds a version header)
const versionHeaderInterceptor: HttpInterceptorFn = (req, next) =&gt;
  next(req.clone({ setHeaders: { 'X-App-Version': '1.0.0' } }));

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes, withComponentInputBinding()),
    provideHttpClient(withInterceptors([versionHeaderInterceptor])),
    provideAnimations(),
    // If you generated the project with --ssr, enable hydration:
    // provideClientHydration(),
  ],
}).catch(err =&gt; console.error(err));</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>provideRouter</strong> wires your routing without a root NgModule.</p>
</li>
<li>
<p>provideHttpClient configures HttpClient with tree-shakable features like withInterceptors.</p>
</li>
<li>
<p>provideAnimations enables Angular animations globally.</p>
</li>
<li>
<p>withComponentInputBinding lets you bind route params/data directly to component inputs.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_routing_with_lazy_standalone_components">Routing with lazy standalone components</h3>
<div class="paragraph">
<p>Define routes in a single place and lazy-load components with loadComponent.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/app.routes.ts
import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () =&gt;
      import('./features/home/home.component').then(m =&gt; m.HomeComponent),
    title: 'Home',
  },
  {
    path: 'projects',
    loadComponent: () =&gt;
      import('./features/projects/projects.component').then(m =&gt; m.ProjectsComponent),
    title: 'Projects',
  },
  { path: '**', redirectTo: '' },
];</code></pre>
</div>
</div>
<div class="paragraph">
<p>This setup is fast to read and easy to evolve. Each path points directly at a standalone component without an intermediate NgModule.</p>
</div>
</div>
<div class="sect2">
<h3 id="_a_minimal_root_component">A minimal root component</h3>
<div class="paragraph">
<p>Keep your root component focused on layout and navigation.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, RouterLink, RouterLinkActive],
  styles: [`
    nav { display: flex; gap: 1rem; padding: 1rem; border-bottom: 1px solid #eee; }
    a.active { font-weight: 600; text-decoration: underline; }
    main { padding: 1rem; }
  `],
  template: `
    &lt;nav&gt;
      &lt;a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }"&gt;Home&lt;/a&gt;
      &lt;a routerLink="/projects" routerLinkActive="active"&gt;Projects&lt;/a&gt;
    &lt;/nav&gt;
    &lt;main&gt;
      &lt;router-outlet /&gt;
    &lt;/main&gt;
  `,
})
export class AppComponent {}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_build_a_real_feature_home_with_forms_and_new_control_flow">Build a real feature: Home with forms and new control flow</h3>
<div class="paragraph">
<p>Use reactive forms and the modern control flow syntax (@if, @for) to keep templates expressive and predictable.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/features/home/home.component.ts
import { Component, signal } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';

type Project = { id: number; name: string };

@Component({
  imports: [ReactiveFormsModule],
  styles: [`
    form { display: flex; gap: 0.5rem; margin: 1rem 0; }
    input { min-width: 240px; }
    ul { padding-left: 1.25rem; }
  `],
  template: `
    &lt;h2&gt;Welcome&lt;/h2&gt;

    &lt;form [formGroup]="searchForm" (ngSubmit)="onSearch()"&gt;
      &lt;input formControlName="q" type="text" placeholder="Search projects" /&gt;
      &lt;button type="submit"&gt;Search&lt;/button&gt;
      &lt;button type="button" (click)="reset()"&gt;Reset&lt;/button&gt;
    &lt;/form&gt;

    @if (results().length === 0) {
      &lt;p&gt;No results found.&lt;/p&gt;
    } @else {
      &lt;ul&gt;
        @for (p of results(); track p.id) {
          &lt;li&gt;{{ p.name }}&lt;/li&gt;
        }
      &lt;/ul&gt;
    }
  `,
})
export class HomeComponent {
  private readonly allProjects: Project[] = [
    { id: 1, name: 'Roadmap' },
    { id: 2, name: 'UI Kit' },
    { id: 3, name: 'Infra' },
  ];

  results = signal&lt;Project[]&gt;(this.allProjects);

  constructor(private fb: FormBuilder) {}

  searchForm = this.fb.nonNullable.group({ q: '' });

  onSearch(): void {
    const q = this.searchForm.value.q?.toLowerCase() ?? '';
    this.results.set(
      this.allProjects.filter(p =&gt; p.name.toLowerCase().includes(q))
    );
  }

  reset(): void {
    this.searchForm.reset({ q: '' });
    this.results.set(this.allProjects);
  }
}</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>signal gives you ergonomic local state without services when you don’t need them.</p>
</li>
<li>
<p>The new control flow removes structural directive noise while remaining explicit.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_a_lazy_projects_page">A lazy projects page</h3>
<div class="paragraph">
<p>Keep feature components focused and stateless by default.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/features/projects/projects.component.ts
import { Component } from '@angular/core';

@Component({
  template: `
    &lt;h2&gt;Projects&lt;/h2&gt;
    &lt;p&gt;This is a lazy-loaded page. Add tables, filters, or charts here.&lt;/p&gt;
  `,
})
export class ProjectsComponent {}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The route configuration we defined earlier will load this component on demand.</p>
</div>
</div>
<div class="sect2">
<h3 id="_http_setup_with_functional_interceptors">HTTP setup with functional interceptors</h3>
<div class="paragraph">
<p>You already provided HttpClient in main.ts. Add domain-specific concerns via functional interceptors as your app grows (auth tokens, correlation IDs, error normalization). They compose with withInterceptors and stay tree-shakable.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// Example: src/app/core/auth.interceptor.ts (optional)
import { HttpInterceptorFn } from '@angular/common/http';

export const authInterceptor: HttpInterceptorFn = (req, next) =&gt; {
  const token = localStorage.getItem('token');
  return next(token ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : req);
};

// Then in main.ts:
// provideHttpClient(withInterceptors([authInterceptor, versionHeaderInterceptor]))</code></pre>
</div>
</div>
<div class="paragraph">
<p>Keep cross-cutting code small, testable, and colocated in core/.</p>
</div>
</div>
<div class="sect2">
<h3 id="_environment_driven_configuration">Environment-driven configuration</h3>
<div class="paragraph">
<p>Use build-time environments for API base URLs and toggles. The CLI swaps files based on configuration.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/environments/environment.ts
export const environment = {
  production: false,
  apiUrl: 'http://localhost:3000/api',
};

// src/environments/environment.prod.ts
export const environment = {
  production: true,
  apiUrl: 'https://api.example.com',
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>Access where needed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// Anywhere in app code
import { environment } from '../environments/environment';
// http.get(`${environment.apiUrl}/projects`)</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_cli_commands_youll_use_daily">CLI commands you’ll use daily</h3>
<div class="ulist">
<ul>
<li>
<p>Generate features: ng g component app/features/activity</p>
</li>
<li>
<p>Add utilities: ng g interceptor core/logging</p>
</li>
<li>
<p>Run locally: ng serve</p>
</li>
<li>
<p>Build for production: ng build --configuration production</p>
</li>
<li>
<p>Run unit tests: ng test</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The Angular CLI keeps project setup consistent while remaining flexible.</p>
</div>
</div>
<div class="sect2">
<h3 id="_optional_server_side_rendering_ssr">Optional: Server-side rendering (SSR)</h3>
<div class="paragraph">
<p>If you create your project with --ssr, the CLI scaffolds an SSR server and hydration for you. In main.ts, include provideClientHydration() so the browser picks up the server-rendered DOM without re-rendering. SSR improves Time to First Byte (TTFB), SEO, and perceived performance for content-heavy routes.</p>
</div>
</div>
<div class="sect2">
<h3 id="_why_this_structure_works">Why this structure works</h3>
<div class="ulist">
<ul>
<li>
<p>Standalone components reduce indirection and speed up onboarding.</p>
</li>
<li>
<p>A single routes file gives you a map of the app at a glance.</p>
</li>
<li>
<p>Providers in main.ts make cross-cutting behavior explicit.</p>
</li>
<li>
<p>Feature-first folders keep implementation details close to their templates and styles.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_troubleshooting_and_tips">Troubleshooting and tips</h3>
<div class="ulist">
<ul>
<li>
<p>Type errors on control flow (@if/@for): ensure your component template uses the new Angular control flow (Angular 17+). No extra imports are needed.</p>
</li>
<li>
<p>RouterLinkActive not working on root: pass [routerLinkActiveOptions]="{ exact: true }" for the root link.</p>
</li>
<li>
<p>Interceptor order: interceptors run in the order provided for requests, and in reverse for responses. Put auth/correlation early; place global error handling last to see the final response state.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_conclusion">Conclusion</h3>
<div class="paragraph">
<p>Angular 20 cuts the ceremony: standalone components by default, functional providers, and minimal bootstrapping. Organize by feature, keep routes clear, and scale without the bloat. The CLI does the grunt work, you build features.</p>
</div>
</div>
<div class="sect2">
<h3 id="_next_steps">Next Steps</h3>
<div class="ulist">
<ul>
<li>
<p>Add a core/ directory for cross-cutting concerns (interceptors, guards, tokens).</p>
</li>
<li>
<p>Introduce a shared/ library for reusable UI primitives.</p>
</li>
<li>
<p>Wire HttpClient to a real backend and centralize API calls behind a data-access service.</p>
</li>
<li>
<p>Enable SSR (--ssr) and hydration for content-rich pages.</p>
</li>
<li>
<p>Adopt signals in more places thoughtfully: state local to a component is a good fit; shared, cross-route state still belongs in services.</p>
</li>
</ul>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Routing in Angular 20: Basics to Navigation Guards</title>
		<link>https://blog.lunatech.com//posts/2025-10-22-routing-in-angular-20-basics-to-navigation-guards</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-10-22T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[Beginner]]></category>
                }
             {
            <category><![CDATA[angular router]]></category>
                }
             {
            <category><![CDATA[navigation]]></category>
                }
             {
            <category><![CDATA[route guards]]></category>
                }
             {
            <category><![CDATA[async redirects]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-10-22-routing-in-angular-20-basics-to-navigation-guards</guid>

					<description>
                        <![CDATA[ Master client-side routing with Angular’s Router. Learn route configuration, navigation, guards, and Angular 20’s asynchronous redirect improvements.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Master client-side routing with Angular’s Router. Learn route configuration, navigation, guards, and Angular 20’s asynchronous redirect improvements.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_why_the_router_still_matters">Why the Router still matters</h3>
<div class="paragraph">
<p>Routing is the backbone of user flow. It defines how screens load, what data they need, and how access is enforced. A reliable setup pays off when your app scales and your team grows. In Angular 20, the Angular Router keeps moving in the right direction: clearer configuration, better async flows, and ergonomics that prioritize maintainability.</p>
</div>
<div class="paragraph">
<p>This guide walks through a practical baseline: route configuration, links, programmatic navigation, route guards, and async redirects, all with standalone components and functional APIs.</p>
</div>
</div>
<div class="sect2">
<h3 id="_a_minimal_modern_router_setup">A minimal, modern router setup</h3>
<div class="paragraph">
<p>Use standalone APIs and <strong>provideRouter</strong> for a clean bootstrap.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(
      routes,
      withPreloading(PreloadAllModules),
    ),
  ],
});</code></pre>
</div>
</div>
<div class="paragraph">
<p>Define your routes in a dedicated file. Prefer lazy-loading to keep initial bundles lean.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// app/app.routes.ts
import { Routes } from '@angular/router';
import { authCanActivate, authCanMatch, unsavedCanDeactivate } from './auth/guards';

export const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },

  {
    path: 'home',
    title: 'Home',
    loadComponent: () =&gt; import('./features/home/home.component').then(m =&gt; m.HomeComponent),
  },
  {
    path: 'dashboard',
    title: 'Dashboard',
    canActivate: [authCanActivate],
    loadComponent: () =&gt; import('./features/dashboard/dashboard.component').then(m =&gt; m.DashboardComponent),
  },
  {
    path: 'projects',
    canMatch: [authCanMatch],
    loadChildren: () =&gt; import('./features/projects/projects.routes').then(m =&gt; m.PROJECTS_ROUTES),
  },
  {
    path: 'settings',
    title: 'Settings',
    canDeactivate: [unsavedCanDeactivate],
    loadComponent: () =&gt; import('./features/settings/settings.component').then(m =&gt; m.SettingsComponent),
  },
  {
    path: 'login',
    title: 'Sign in',
    loadComponent: () =&gt; import('./features/auth/login.component').then(m =&gt; m.LoginComponent),
  },
  {
    path: '**',
    title: 'Not Found',
    loadComponent: () =&gt; import('./shared/not-found/not-found.component').then(m =&gt; m.NotFoundComponent),
  },
];</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_application_shell_links_and_outlet">The application shell: links and outlet</h3>
<div class="paragraph">
<p>Make navigation obvious and accessible. Use RouterLink and RouterLinkActive for feedback.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;!-- app/app.component.html --&gt;
&lt;header class="app-header"&gt;
  &lt;nav&gt;
    &lt;a routerLink="/home" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }"&gt;Home&lt;/a&gt;
    &lt;a routerLink="/dashboard" routerLinkActive="active"&gt;Dashboard&lt;/a&gt;
    &lt;a routerLink="/projects" routerLinkActive="active"&gt;Projects&lt;/a&gt;
    &lt;a routerLink="/settings" routerLinkActive="active"&gt;Settings&lt;/a&gt;
  &lt;/nav&gt;
&lt;/header&gt;

&lt;main&gt;
  &lt;router-outlet&gt;&lt;/router-outlet&gt;
&lt;/main&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>RouterLink keeps your templates declarative; RouterOutlet hosts the matched view. For accessibility and UX, the active class communicates where the user is during navigation.</p>
</div>
</div>
<div class="sect2">
<h3 id="_a_realistic_feature_route_with_params_and_lazy_loading">A realistic feature route with params and lazy-loading</h3>
<div class="paragraph">
<p>For features like Projects, lazy load the route definition and child screens.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// features/projects/projects.routes.ts
import { Routes } from '@angular/router';

export const PROJECTS_ROUTES: Routes = [
  {
    path: '',
    title: 'Projects',
    loadComponent: () =&gt; import('./list/projects-list.component').then(m =&gt; m.ProjectsListComponent),
  },
  {
    path: ':id',
    title: 'Project Details',
    loadComponent: () =&gt; import('./details/project-details.component').then(m =&gt; m.ProjectDetailsComponent),
  },
];</code></pre>
</div>
</div>
<div class="paragraph">
<p>In a details component, read route params. Use clean, testable patterns.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// features/projects/details/project-details.component.ts
import { Component, computed, inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { ProjectService } from '../data/project.service';

@Component({
  standalone: true,
  selector: 'app-project-details',
  template: `
    @if (project(); as data) {
      &lt;h2&gt;{{ data.name }}&lt;/h2&gt;
      &lt;button (click)="goBack()"&gt;Back&lt;/button&gt;
    } @else {
      &lt;p&gt;Project not found.&lt;/p&gt;
    }
  `,
})
export class ProjectDetailsComponent {
  private route = inject(ActivatedRoute);
  private router = inject(Router);
  private service = inject(ProjectService);

  private paramMap = toSignal(this.route.paramMap, { initialValue: this.route.snapshot.paramMap });
  id = computed(() =&gt; this.paramMap()?.get('id') ?? null);

  project = toSignal(this.service.getProjectStream(this.route.paramMap), { initialValue: null });

  goBack() {
    this.router.navigate(['../'], { relativeTo: this.route });
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Notes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Use toSignal when a component benefits from reactive values in the template.</p>
</li>
<li>
<p>Keep navigation relative to avoid hardcoding URLs.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_programmatic_navigation_that_reads_well">Programmatic navigation that reads well</h3>
<div class="paragraph">
<p>When you need to route from code, favor clarity and explicitness.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// features/auth/login.component.ts (snippet)
import { Component, inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Component({
  standalone: true,
  template: `
    &lt;form (ngSubmit)="login()"&gt;
      &lt;!-- form fields --&gt;
      &lt;button type="submit"&gt;Sign in&lt;/button&gt;
    &lt;/form&gt;
  `,
})
export class LoginComponent {
  private router = inject(Router);
  private route = inject(ActivatedRoute);
  private auth = inject(AuthService);

  async login() {
    await this.auth.signIn();
    const redirect = this.route.snapshot.queryParamMap.get('redirect') || '/dashboard';
    this.router.navigateByUrl(redirect, { replaceUrl: true, state: { from: 'login' } });
  }
}</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>navigate and navigateByUrl are both fine; navigate works with command arrays and relative routes.</p>
</li>
<li>
<p>replaceUrl avoids filling history with transient steps like login.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_route_guards_protect_match_and_confirm">Route guards: protect, match, and confirm</h3>
<div class="paragraph">
<p>Functional guards are terse, testable, and DI-friendly. Use canActivate for already-loaded routes and canMatch to gate access before lazy-loading. Use canDeactivate to protect against losing changes.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// app/auth/guards.ts
import { inject } from '@angular/core';
import { CanActivateFn, CanDeactivateFn, CanMatchFn, Router, UrlSegment, Route } from '@angular/router';
import { AuthService } from './auth.service';

export const authCanActivate: CanActivateFn = (_route, state) =&gt; {
  const auth = inject(AuthService);
  const router = inject(Router);
  if (auth.isLoggedIn()) return true;
  // Return a UrlTree to redirect without throwing
  return router.createUrlTree(['/login'], { queryParams: { redirect: state.url } });
};

export const authCanMatch: CanMatchFn = (route: Route, segments: UrlSegment[]) =&gt; {
  const auth = inject(AuthService);
  const router = inject(Router);
  if (auth.isLoggedIn()) return true;

  const attempted = '/' + segments.map(s =&gt; s.path).join('/');
  return router.createUrlTree(['/login'], { queryParams: { redirect: attempted } });
};

export const unsavedCanDeactivate: CanDeactivateFn&lt;{ hasUnsavedChanges(): boolean }&gt; = (component) =&gt; {
  return component.hasUnsavedChanges()
    ? confirm('You have unsaved changes. Leave this page?')
    : true;
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>Notes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Returning a UrlTree is the most ergonomic way to redirect from guards.</p>
</li>
<li>
<p>Prefer canMatch for lazy-loaded feature entries. It prevents loading code the user can’t access.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_async_redirects_in_angular_20">Async redirects in Angular 20</h3>
<div class="paragraph">
<p>Redirects are often conditional and data-driven. Angular 20 refines ergonomics for async redirects by allowing redirect functions to return a Promise or Observable of a UrlTree, keeping logic declarative in the route table. When you need Dependency Injection, the function executes in a proper injection context.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// app/app.routes.ts (excerpt showing async redirect)
import { Router, Routes } from '@angular/router';
import { inject } from '@angular/core';
import { AuthService } from './auth/auth.service';

export const routes: Routes = [
  // ...
  {
    path: 'start',
    // Async redirect based on login status
    redirectTo: async () =&gt; {
      const auth = inject(AuthService);
      const router = inject(Router);
      const isLoggedIn = await auth.isLoggedInOnce(); // e.g., resolves after token refresh
      return isLoggedIn
        ? router.createUrlTree(['/dashboard'])
        : router.createUrlTree(['/login'], { queryParams: { redirect: '/start' } });
    },
    pathMatch: 'full',
  },
  // ...
];</code></pre>
</div>
</div>
<div class="paragraph">
<p>If your team prefers explicit control (or for compatibility), use canMatch to perform the same async decision and return a UrlTree. Both approaches keep “redirect intent” within the routing layer, preserving a clear separation from UI.</p>
</div>
</div>
<div class="sect2">
<h3 id="_handling_404s_and_fallbacks">Handling 404s and fallbacks</h3>
<div class="paragraph">
<p>Keep a final catch-all route at the end. Make the not-found component tiny and isolated.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// shared/not-found/not-found.component.ts
import { Component } from '@angular/core';

@Component({
  standalone: true,
  template: `
    &lt;h2&gt;Page not found&lt;/h2&gt;
    &lt;p&gt;The page you’re looking for doesn’t exist.&lt;/p&gt;
    &lt;a routerLink="/home"&gt;Go to Home&lt;/a&gt;
  `,
})
export class NotFoundComponent {}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_developer_experience_tips_that_scale">Developer experience tips that scale</h3>
<div class="ulist">
<ul>
<li>
<p>Title and data: Use the route title for sensible defaults. Keep route data small and serializable.</p>
</li>
<li>
<p>Preloading: withPreloading(PreloadAllModules) improves perceived speed after the first screen.</p>
</li>
<li>
<p>Guard reuse: Share a single predicate behind multiple guard types (e.g., canActivate and canMatch) to avoid drift.</p>
</li>
<li>
<p>UrlTree over side effects: Prefer returning UrlTree over imperative router.navigate inside guards.</p>
</li>
<li>
<p>Relative navigation: Favor relativeTo where possible to avoid brittle absolute paths.</p>
</li>
<li>
<p>Observability: Listen to router.events for tracing NavigationStart, NavigationEnd, NavigationCancel when debugging.</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// Simple router event logging helper (dev only)
import { filter } from 'rxjs';
import { Router, NavigationStart, NavigationEnd, NavigationCancel } from '@angular/router';

export function logNavigation(router: Router) {
  router.events
    .pipe(filter(e =&gt; e instanceof NavigationStart || e instanceof NavigationEnd || e instanceof NavigationCancel))
    .subscribe(e =&gt; console.debug('[router]', e));
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_common_pitfalls_and_guardrails">Common pitfalls and guardrails</h3>
<div class="ulist">
<ul>
<li>
<p>Infinite redirect loops: Always check target vs current URL when redirecting conditionally. Returning the same UrlTree repeatedly will stall navigation.</p>
</li>
<li>
<p>Missing pathMatch: For empty-path redirects, pathMatch: 'full' prevents partial matches from hijacking nested routes.</p>
</li>
<li>
<p>Eager vs lazy: Use canMatch for feature entries to avoid loading code for unauthorized users.</p>
</li>
<li>
<p>Query params and state: Preserve user intent via redirect query params. Clear them after successful navigation if they’re transient.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_putting_it_together_a_cohesive_flow">Putting it together: a cohesive flow</h3>
<div class="ulist">
<ul>
<li>
<p>Users hitting /start are asynchronously redirected based on auth state using async redirects.</p>
</li>
<li>
<p>Unauthenticated users attempting /projects are intercepted by canMatch and sent to /login with a redirect back.</p>
</li>
<li>
<p>Settings protects against accidental loss via canDeactivate.</p>
</li>
<li>
<p>Dashboard and Projects load lazily, preloaded after the first screen.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This balances UX and performance while keeping the routing layer the single source of truth for navigation logic.</p>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A well-structured router is more than a URL switchboard, it’s an agreement about flow, access, and intent. Angular’s modern APIs make routing readable and testable: standalone routes, functional route guards, lazy-loading, and async redirects that keep decisions close to configuration. When your code communicates clearly, your team moves faster and makes fewer mistakes.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_next_steps">Next Steps</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Extract guard predicates into pure functions and unit test them with dependency mocks.</p>
</li>
<li>
<p>Add a data resolver where you need the route to wait for critical data before rendering.</p>
</li>
<li>
<p>Introduce a prefetch strategy for specific features, or preload on hover using quicklink-style heuristics.</p>
</li>
<li>
<p>Explore view transitions with router integration to smooth screen changes.</p>
</li>
<li>
<p>Audit routes for consistent titles, data contracts, and error handling paths.</p>
</li>
</ul>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Data Binding in Angular: Templates, Interpolation, and Property/Event Binding</title>
		<link>https://blog.lunatech.com//posts/2025-10-15-data-binding-in-angular-templates-interpolation-and-propertyevent-binding</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-10-15T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[Beginner]]></category>
                }
             {
            <category><![CDATA[property binding]]></category>
                }
             {
            <category><![CDATA[event binding]]></category>
                }
             {
            <category><![CDATA[interpolation]]></category>
                }
             {
            <category><![CDATA[template syntax]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-10-15-data-binding-in-angular-templates-interpolation-and-propertyevent-binding</guid>

					<description>
                        <![CDATA[ Data binding is the heartbeat of Angular templates. Learn how data flows between components and templates using interpolation, property binding, event binding, and two-way binding. This topic also introduces new Angular 20 template features like template literals. We’ll keep the focus practical: clear patterns, minimal surprises, and maintainable code that scales.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Data binding is the heartbeat of Angular templates. Learn how data flows between components and templates using interpolation, property binding, event binding, and two-way binding. This topic also introduces new Angular 20 template features like template literals. We’ll keep the focus practical: clear patterns, minimal surprises, and maintainable code that scales.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_mental_model_unidirectional_data_flow_well_timed_feedback">The mental model: unidirectional data flow, well-timed feedback</h3>
<div class="ulist">
<ul>
<li>
<p>From component to view: interpolation and property binding render state.</p>
</li>
<li>
<p>From view to component: event binding captures user intent.</p>
</li>
<li>
<p>Two-way binding is a convenience that composes the two, not a separate mechanism.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Favor predictable one-way flows; reach for two-way when it improves clarity without hiding complexity.</p>
</div>
</div>
<div class="sect2">
<h3 id="_interpolation_simple_readable_one_way_display">Interpolation: simple, readable, one-way display</h3>
<div class="paragraph">
<p>Interpolation renders values into text nodes using template syntax like {{ &#8230;&#8203; }}. It’s ideal for human-readable content.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;h2&gt;{{ title }}&lt;/h2&gt;
&lt;p&gt;Welcome, {{ user?.name ?? 'Guest' }}.&lt;/p&gt;
&lt;p&gt;Subtotal: {{ price | currency: 'USD' }}&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Guidelines:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Keep expressions simple. Avoid calling methods that do non-trivial work.</p>
</li>
<li>
<p>Use pipes for presentation logic (formatting, slicing, etc.).</p>
</li>
<li>
<p>Use the safe-navigation operator (?.) to avoid null/undefined errors at render time.</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_angular_20_template_literals_in_expressions">Angular 20 template literals in expressions</h4>
<div class="paragraph">
<p>Angular 20 enables JavaScript-style template literals inside template expressions for more expressive string composition.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;h2&gt;{{ `${greeting}, ${user?.firstName || 'friend'}!` }}&lt;/h2&gt;
&lt;button [attr.aria-label]="`Open details for ${item.title}`"&gt;Open&lt;/button&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Use them for readable, localized strings or ARIA attributes. Keep them concise. If you’re building complex messages, consider a dedicated formatter or i18n.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_property_binding_write_to_dom_and_component_inputs">Property binding: write to DOM and component inputs</h3>
<div class="paragraph">
<p>Property binding uses square brackets to set DOM properties, ARIA attributes, styles, classes, and child component inputs.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;!-- DOM properties --&gt;
&lt;input [value]="query()" [disabled]="isSubmitting()" /&gt;

&lt;!-- Attributes and accessibility --&gt;
&lt;button [attr.aria-pressed]="isActive" [attr.data-id]="item.id"&gt;Toggle&lt;/button&gt;

&lt;!-- Classes and styles --&gt;
&lt;div [class.active]="isActive" [style.width.px]="panelWidth"&gt;&lt;/div&gt;

&lt;!-- Child component inputs --&gt;
&lt;app-avatar [src]="user.photoUrl" [alt]="user.name" [size]="64"&gt;&lt;/app-avatar&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Under the hood, Angular binds to the actual property when available (safer and more correct than raw attributes). Use [attr.*] only when there is no matching property.</p>
</div>
<div class="sect3">
<h4 id="_signals_play_nicely_with_bindings">Signals play nicely with bindings</h4>
<div class="paragraph">
<p>Signals provide push-based reactivity. Call them in templates to read their current value.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-search',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    &lt;label&gt;
      Query:
      &lt;input [value]="query()" (input)="onInput($event)" placeholder="Type to search..." /&gt;
    &lt;/label&gt;

    @if (filtered().length === 0) {
      &lt;p&gt;No results&lt;/p&gt;
    } @else {
      &lt;ul&gt;
        @for (item of filtered(); track item) {
          &lt;li&gt;{{ item }}&lt;/li&gt;
        }
      &lt;/ul&gt;
    }
  `
})
export class SearchComponent {
  readonly items = signal(['apple', 'banana', 'citrus', 'date', 'elderberry']);
  readonly query = signal('');
  readonly filtered = computed(() =&gt;
    this.items().filter(v =&gt; v.toLowerCase().includes(this.query().toLowerCase()))
  );

  onInput(event: Event) {
    const input = event.target as HTMLInputElement;
    this.query.set(input.value);
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Property binding [value]="query()" reads the signal.</p>
</li>
<li>
<p>Event binding (input)="&#8230;&#8203;" updates the signal.</p>
</li>
<li>
<p>Built-in control flow (@if and @for) keeps the template declarative and fast.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_event_binding_user_intent_back_to_the_component">Event binding: user intent back to the component</h3>
<div class="paragraph">
<p>Event binding uses parentheses to listen to DOM or component events.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;button (click)="toggle()"&gt;{{ isOpen ? 'Close' : 'Open' }}&lt;/button&gt;
&lt;input (keydown.enter)="submit()" /&gt;
&lt;input (input)="onInput($event)" /&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Type your handlers for safety and clarity.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">toggle(): void {
  this.isOpen = !this.isOpen;
}

submit(): void {
  if (!this.formValid) return;
  this.save();
}

onInput(event: Event): void {
  const target = event.target as HTMLInputElement;
  this.value = target.value;
}</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Prefer specific event names like keydown.enter for intent-revealing handlers.</p>
</li>
<li>
<p>Avoid expensive work inside template expressions; do it in component methods or computed signals.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_two_way_binding_when_it_helps_use_it_intentionally">Two-way binding: when it helps, use it intentionally</h3>
<div class="paragraph">
<p>Two-way binding composes property binding and event binding into a single banana-in-a-box syntax.</p>
</div>
<div class="sect3">
<h4 id="_with_forms_ngmodel">With forms (ngModel)</h4>
<div class="paragraph">
<p>This is convenient in simple forms. Import FormsModule in standalone components.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-preferences',
  standalone: true,
  imports: [FormsModule],
  template: `
    &lt;label&gt;Nickname: &lt;input [(ngModel)]="nickname" /&gt;&lt;/label&gt;
    &lt;label&gt;
      &lt;input type="checkbox" [(ngModel)]="emailOptIn" /&gt;
      Email me updates
    &lt;/label&gt;
    &lt;p&gt;Preview: {{ nickname }} (opt-in: {{ emailOptIn }})&lt;/p&gt;
  `
})
export class PreferencesComponent {
  nickname = '';
  emailOptIn = true;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>With signals, bridge two-way binding by pairing [ngModel] and (ngModelChange):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;input [ngModel]="query()" (ngModelChange)="query.set($event)" /&gt;</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_custom_two_way_binding_for_child_components">Custom two-way binding for child components</h4>
<div class="paragraph">
<p>Define a pair of Input and Output with the Change suffix. Angular will recognize [(value)].</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-rating',
  standalone: true,
  imports: [CommonModule],
  template: `
    &lt;div class="stars"&gt;
      &lt;button *ngFor="let s of [1,2,3,4,5]"
              type="button"
              [class.filled]="s &lt;= value"
              (click)="setValue(s)"
              [attr.aria-label]="'Set rating to ' + s"&gt;
        ★
      &lt;/button&gt;
    &lt;/div&gt;
  `,
  styles: [`.filled { color: #f59e0b; } button { background:none; border:none; font-size:1.25rem; cursor:pointer; }`]
})
export class RatingComponent {
  @Input() value = 0;
  @Output() valueChange = new EventEmitter&lt;number&gt;();

  setValue(v: number) {
    this.value = v;
    this.valueChange.emit(v);
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Parent usage:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;app-rating [(value)]="product.rating"&gt;&lt;/app-rating&gt;
&lt;p&gt;Current rating: {{ product.rating }}&lt;/p&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Tip: If your project uses the signal-based inputs API, the model() helper can reduce boilerplate. The classic pair above remains broadly compatible and explicit.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_template_syntax_patterns_that_scale">Template syntax patterns that scale</h3>
<div class="ulist">
<ul>
<li>
<p>Keep expressions pure and cheap. Avoid calling functions that allocate arrays or perform filtering on each change detection; use computed signals or memoized selectors.</p>
</li>
<li>
<p>Bind to properties, not attributes. Prefer [value], [disabled], [checked] over [attr.value], except when no property exists.</p>
</li>
<li>
<p>Use track with @for to avoid re-rendering stable items:
@for (item of items; track item.id) { &#8230;&#8203; }</p>
</li>
<li>
<p>For [innerHTML], sanitize or trust only safe content. Angular’s DomSanitizer exists for exceptional cases—prefer plain text whenever possible.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_angular_20_template_literal_examples_in_context">Angular 20 template literal examples in context</h3>
<div class="paragraph">
<p>Use template literals where they improve clarity, especially for ARIA and labeling.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;!-- Readable, localized-friendly labels --&gt;
&lt;button
  type="button"
  [attr.aria-label]="`${isPlaying ? 'Pause' : 'Play'} ${track.title}`"
  (click)="togglePlay()"&gt;
  {{ isPlaying ? 'Pause' : 'Play' }}
&lt;/button&gt;

&lt;!-- Combining with pipes --&gt;
&lt;p&gt;{{ `Hello, ${user?.name || 'Guest'}` | titlecase }}&lt;/p&gt;

&lt;!-- Attributes that aren’t DOM properties --&gt;
&lt;a [attr.data-test-id]="`link-${link.id}`" [href]="link.url"&gt;{{ link.title }}&lt;/a&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Keep logic minimal; push complex decisions into the component or computed signals for testability.</p>
</div>
</div>
<div class="sect2">
<h3 id="_a_cohesive_example_binding_the_pieces">A cohesive example: binding the pieces</h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RatingComponent } from './rating.component';

@Component({
  selector: 'app-product-card',
  standalone: true,
  imports: [CommonModule, FormsModule, RatingComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    &lt;article class="card"&gt;
      &lt;header&gt;
        &lt;h3&gt;{{ product().name }}&lt;/h3&gt;
        &lt;p class="price"&gt;{{ product().price | currency:'USD' }}&lt;/p&gt;
      &lt;/header&gt;

      &lt;label [attr.aria-label]="`Quantity for ${product().name}`"&gt;
        Qty:
        &lt;input type="number"
               min="1"
               [ngModel]="qty()"
               (ngModelChange)="qty.set($event)" /&gt;
      &lt;/label&gt;

      &lt;app-rating [(value)]="rating"&gt;&lt;/app-rating&gt;

      &lt;p&gt;{{ description }}&lt;/p&gt;

      &lt;button type="button"
              class="add"
              [disabled]="isSubmitting()"
              (click)="addToCart()"&gt;
        {{ isSubmitting() ? 'Adding…' : 'Add to cart' }}
      &lt;/button&gt;
    &lt;/article&gt;
  `,
  styles: [`.card{border:1px solid #e5e7eb;border-radius:.5rem;padding:1rem}.price{color:#374151}.add{margin-top:.75rem}`]
})
export class ProductCardComponent {
  readonly product = signal({ id: 1, name: 'Noise-canceling Headphones', price: 199.99 });
  readonly qty = signal(1);
  rating = 4; // two-way bound to child
  readonly isSubmitting = signal(false);

  get description(): string {
    return `${this.product().name} — rated ${this.rating}/5 by our customers.`;
  }

  async addToCart() {
    this.isSubmitting.set(true);
    try {
      await fakeHttpPost({
        id: this.product().id,
        qty: this.qty(),
        rating: this.rating
      });
    } finally {
      this.isSubmitting.set(false);
    }
  }
}

function fakeHttpPost(payload: unknown) {
  return new Promise&lt;void&gt;(r =&gt; setTimeout(r, 500));
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>What you see:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Interpolation renders human-readable fields.</p>
</li>
<li>
<p>Property binding controls input state and disabled state.</p>
</li>
<li>
<p>Event binding processes clicks and model changes.</p>
</li>
<li>
<p>Two-way binding streamlines rating updates.</p>
</li>
<li>
<p>Template literals keep labels clear and accessible.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_conclusion">Conclusion</h3>
<div class="paragraph">
<p>Data binding is how intent flows through your Angular app. Interpolation and property binding project state. Event binding captures change. Two-way binding composes both when it reduces friction. With Angular 20’s template literal support, you can craft clearer strings and ARIA labels without sacrificing readability. Keep expressions simple, push complexity into components or computed signals, and lean on built-in control flow for performance.</p>
</div>
</div>
<div class="sect2">
<h3 id="_next_steps">Next Steps</h3>
<div class="ulist">
<ul>
<li>
<p>Audit a component for heavy template expressions. Move logic into computed signals or pure methods.</p>
</li>
<li>
<p>Add ARIA attributes with property binding and template literals to improve accessibility.</p>
</li>
<li>
<p>Refactor a custom control to support [(value)] using the input/output pair.</p>
</li>
<li>
<p>Explore Angular’s built-in control flow (@if, @for, @switch) to reduce directive boilerplate.</p>
</li>
<li>
<p>If your team uses signals broadly, standardize patterns for bridging with forms: [ngModel] + (ngModelChange) for crisp two-way behavior.</p>
</li>
</ul>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Getting Started with Angular 19: Your First Signals-Powered App</title>
		<link>https://blog.lunatech.com//posts/2025-10-12-getting-started-with-angular-19--your-first-signals-powered-app</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-10-12T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[angular basics]]></category>
                }
             {
            <category><![CDATA[signals]]></category>
                }
             {
            <category><![CDATA[reactive state]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-10-12-getting-started-with-angular-19--your-first-signals-powered-app</guid>

					<description>
                        <![CDATA[ Angular 19 makes signals a first-class, ergonomic way to manage reactive state—without the ceremony many of us associate with using RxJS for every problem. In this guide, you’ll build a small, production-quality feature using signals, computed values, and effects, sticking to Angular basics while practicing patterns you’ll reuse in real apps. Think of it as your “hello world” for signals, with enough structure to scale.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Angular 19 makes signals a first-class, ergonomic way to manage reactive state—without the ceremony many of us associate with using RxJS for every problem. In this guide, you’ll build a small, production-quality feature using signals, computed values, and effects, sticking to Angular basics while practicing patterns you’ll reuse in real apps. Think of it as your “hello world” for signals, with enough structure to scale.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_why_signals_for_your_first_angular_19_app">Why signals for your first Angular 19 app</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Signals shine when:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>You want local or feature-level state that’s easy to understand and test.</p>
</li>
<li>
<p>You prefer declarative data flow with minimal boilerplate.</p>
</li>
<li>
<p>You care about performance and predictable change detection.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Signals give you:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>signal(): a mutable value you set and read like a variable.</p>
</li>
<li>
<p>computed(): a derived value that automatically recalculates when dependencies change.</p>
</li>
<li>
<p>effect(): a side effect that runs when its dependent signals change (think persistence, logging, analytics).</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>With the built-in control flow (@if, @for, @switch), Angular templates read signals naturally and concisely—no async pipe or manual subscriptions required.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_were_building">What we’re building</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A tiny yet complete “Tasks” feature:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Add tasks, toggle done, filter by status.</p>
</li>
<li>
<p>Persist to localStorage.</p>
</li>
<li>
<p>Derive stats via computed signals.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>We’ll use:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Standalone components.</p>
</li>
<li>
<p>inject() for dependency injection.</p>
</li>
<li>
<p>Signals for reactive state.</p>
</li>
<li>
<p>New template control flow.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If you’re comfortable with Angular basics (components, templates, DI), you’re good to go.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_app_structure">App structure</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>TaskStore: a signals-powered service for state and business logic.</p>
</li>
<li>
<p>AppComponent: a standalone component that renders the UI and interacts with the store.</p>
</li>
<li>
<p>main.ts: bootstraps the app.</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_taskstore_reactive_state_with_signals">TaskStore: reactive state with signals</h3>
<div class="paragraph">
<p>We’ll keep domain logic, mutations, and persistence here—a clean separation from the component.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Injectable, computed, effect, signal } from '@angular/core';

export type TaskFilter = 'all' | 'open' | 'done';

export interface Task {
  id: string;
  title: string;
  done: boolean;
}

function loadTasks(): Task[] {
  try {
    const raw = localStorage.getItem('tasks.v1');
    return raw ? JSON.parse(raw) as Task[] : [];
  } catch {
    return [];
  }
}

@Injectable({ providedIn: 'root' })
export class TaskStore {
  // Source signals
  private readonly tasks = signal&lt;Task[]&gt;(loadTasks());
  readonly filter = signal&lt;TaskFilter&gt;('all');

  // Derived reactive state
  readonly stats = computed(() =&gt; {
    const list = this.tasks();
    const done = list.filter(t =&gt; t.done).length;
    const open = list.length - done;
    return { total: list.length, open, done };
  });

  readonly filtered = computed(() =&gt; {
    const f = this.filter();
    const list = this.tasks();
    if (f === 'open') return list.filter(t =&gt; !t.done);
    if (f === 'done') return list.filter(t =&gt; t.done);
    return list;
  });

  // Persist tasks whenever they change
  private readonly persist = effect(() =&gt; {
    const current = this.tasks();
    localStorage.setItem('tasks.v1', JSON.stringify(current));
  });

  add(title: string) {
    const clean = title.trim();
    if (!clean) return;
    const newTask: Task = { id: crypto.randomUUID(), title: clean, done: false };
    this.tasks.update(list =&gt; [newTask, ...list]);
  }

  toggle(id: string) {
    this.tasks.update(list =&gt;
      list.map(t =&gt; (t.id === id ? { ...t, done: !t.done } : t))
    );
  }

  remove(id: string) {
    this.tasks.update(list =&gt; list.filter(t =&gt; t.id !== id));
  }

  clearDone() {
    this.tasks.update(list =&gt; list.filter(t =&gt; !t.done));
  }

  // Expose readonly getters where helpful
  get all() { return this.tasks.asReadonly(); }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Notes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Keep tasks private, expose asReadonly() if needed, and route all mutations through methods. That protects invariants and makes tests straightforward.</p>
</li>
<li>
<p>computed() caches until dependencies change, so stats and filtered are cheap to read in templates.</p>
</li>
<li>
<p>effect() is for side effects only. Don’t derive data there.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_appcomponent_template_first_with_control_flow">AppComponent: template-first with control flow</h3>
<div class="paragraph">
<p>The component stays lean: read signals, call store methods, and keep the markup honest. No subscriptions and no manual change detection.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { Component, inject } from '@angular/core';
import { TaskStore, TaskFilter } from './task.store';

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html',
})
export class AppComponent {
  readonly store = inject(TaskStore);

  setFilter(f: TaskFilter) {
    this.store.filter.set(f);
  }

  add(input: HTMLInputElement) {
    this.store.add(input.value);
    input.value = '';
    input.focus();
  }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;header class="app-header"&gt;
  &lt;h1&gt;Tasks (Signals)&lt;/h1&gt;
  &lt;p&gt;
    Total: {{ store.stats().total }}
    • Open: {{ store.stats().open }}
    • Done: {{ store.stats().done }}
  &lt;/p&gt;
&lt;/header&gt;

&lt;section class="task-create"&gt;
  &lt;input
    #title
    type="text"
    placeholder="Add a task and press Enter"
    (keyup.enter)="add(title)"
    aria-label="New task title" /&gt;
  &lt;button (click)="add(title)"&gt;Add&lt;/button&gt;
&lt;/section&gt;

&lt;nav class="filters"&gt;
  &lt;button (click)="setFilter('all')" [class.active]="store.filter() === 'all'"&gt;All&lt;/button&gt;
  &lt;button (click)="setFilter('open')" [class.active]="store.filter() === 'open'"&gt;Open&lt;/button&gt;
  &lt;button (click)="setFilter('done')" [class.active]="store.filter() === 'done'"&gt;Done&lt;/button&gt;
&lt;/nav&gt;

&lt;section class="task-list"&gt;
  @if (store.filtered().length === 0) {
    &lt;p class="empty"&gt;No tasks to show.&lt;/p&gt;
  } @else {
    &lt;ul&gt;
      @for (task of store.filtered(); track task.id) {
        &lt;li&gt;
          &lt;label&gt;
            &lt;input type="checkbox" [checked]="task.done" (change)="store.toggle(task.id)" /&gt;
            &lt;span [class.done]="task.done"&gt;{{ task.title }}&lt;/span&gt;
          &lt;/label&gt;
          &lt;button class="remove" (click)="store.remove(task.id)" aria-label="Remove task"&gt;✕&lt;/button&gt;
        &lt;/li&gt;
      }
    &lt;/ul&gt;
  }
&lt;/section&gt;

&lt;footer class="actions"&gt;
  &lt;button (click)="store.clearDone()" [disabled]="store.stats().done === 0"&gt;
    Clear completed
  &lt;/button&gt;
&lt;/footer&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>A few things to notice:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Reading a signal in a template uses function-call syntax: store.stats().</p>
</li>
<li>
<p>The new @if and @for syntax is concise and fast; track by a stable id to minimize DOM churn.</p>
</li>
<li>
<p>We avoid ngModel here to keep the example lean; use reactive forms if you need validation and composition.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_bootstrap">Bootstrap</h3>
<div class="paragraph">
<p>With standalone components, the bootstrap is pleasantly thin.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';

bootstrapApplication(AppComponent).catch(err =&gt; console.error(err));</code></pre>
</div>
</div>
<div class="paragraph">
<p>That’s it. No NgModule, no extra ceremony. In a larger app, you’d layer in provideRouter, HTTP, and other providers here.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_developer_experience_and_design_notes">Developer experience and design notes</h2>
<div class="sectionbody">
<div class="paragraph">
<p>From real projects migrating to signals and the new control flow, a few principles keep teams productive:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Keep mutations small and intention-revealing.</p>
</li>
<li>
<p>add, toggle, remove, clearDone communicate behavior explicitly.</p>
</li>
<li>
<p>Encapsulate shape and invariants in your store; keep the component mostly declarative.</p>
</li>
<li>
<p>Use computed for any value you want to “feel like” state in the template.</p>
</li>
<li>
<p>stats and filtered are cheap to read and always consistent.</p>
</li>
<li>
<p>Avoid doing ad-hoc filtering in templates; it’s harder to optimize and test.</p>
</li>
<li>
<p>Reserve effect for I/O and cross-cutting behavior.</p>
</li>
<li>
<p>Persistence, analytics, message bus publishing—great uses of effect.</p>
</li>
<li>
<p>Don’t compute UI data inside effect; that’s what computed is for.</p>
</li>
<li>
<p>Prefer signals for local/feature reactive state, embrace RxJS where it fits.</p>
</li>
<li>
<p>Signals are excellent for UI state and domain mutations.</p>
</li>
<li>
<p>Streams still shine for event composition, websockets, and complex async flows.</p>
</li>
<li>
<p>Interop utilities exist if you need to bridge; start simple.</p>
</li>
<li>
<p>Track by stable ids in @for.</p>
</li>
<li>
<p>You’ll avoid unnecessary re-renders and improve perceived performance.</p>
</li>
<li>
<p>Keep templates dumb.</p>
</li>
<li>
<p>Let the store manage logic; your templates will stay readable, and testing gets easier.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_testing_the_store">Testing the store</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Signals play nicely with unit tests because there’s no hidden subscription machinery to manage.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Instantiate TaskStore directly, call methods, and assert on taskStore.filtered(), taskStore.stats(), etc.</p>
</li>
<li>
<p>If an effect writes to localStorage, consider injecting a light persistence adapter so you can stub it in tests. For this article’s simplicity, we wrote to localStorage directly; in production, prefer an injected storage port.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_where_this_scales">Where this scales</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This pattern scales surprisingly far:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Add tags or due dates? Extend Task and update computed accordingly.</p>
</li>
<li>
<p>Need a multi-page app? Provide routes and lazy-load feature components while keeping a small, focused store per feature.</p>
</li>
<li>
<p>Want undo/redo? Wrap mutations to capture patches and provide intent-level commands.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Signals help you move faster because the mental model is simple: read values, change values, derive values. It’s the right default for many UI flows.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_common_pitfalls_to_avoid">Common pitfalls to avoid</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Overusing effect for data derivation. If you find yourself setting signals inside an effect just to compute something, reach for computed instead.</p>
</li>
<li>
<p>Mixing many mutable signals in components. Prefer a single cohesive store per feature or sub-feature.</p>
</li>
<li>
<p>Forgetting to track by id in @for. It’s a small habit with big performance wins.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Getting started with Angular 19 and signals doesn’t require a framework rewrite. By leaning on a simple TaskStore and a lean component using the new control flow, we built a small but complete feature with clear reactive state and minimal boilerplate. This is the kind of foundation that keeps teams sane as apps grow—explicit mutations, derived state where it belongs, and templates that read like a story.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_next_steps">Next Steps</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Add a search signal and a computed that combines filter + search.</p>
</li>
<li>
<p>Extract persistence into an injected storage service and mock it in unit tests.</p>
</li>
<li>
<p>Introduce provideRouter and split the UI into feature routes.</p>
</li>
<li>
<p>Integrate reactive forms for validation on create/edit flows.</p>
</li>
<li>
<p>Explore interop with RxJS for server events or HTTP polling, using signals at the edges.</p>
</li>
<li>
<p>Measure with Angular DevTools and keep track-by rules tight as lists grow.</p>
</li>
</ul>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Terminal-first Mouseless Development Or How To Be A Hipster Engineer</title>
		<link>https://blog.lunatech.com//posts/2025-07-11-terminal-first-mouseless-development-or-how-to-be-hipster-engineer</link>
		
		<dc:creator><![CDATA[]]></dc:creator>
        <pubDate>2025-07-11T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[beyond-the-code]]></category>
                }
             {
            <category><![CDATA[bash-to-the-feature]]></category>
                }
             {
            <category><![CDATA[vim]]></category>
                }
             {
            <category><![CDATA[tmux]]></category>
                }
             {
            <category><![CDATA[cli]]></category>
                }
             {
            <category><![CDATA[terminal]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-07-11-terminal-first-mouseless-development-or-how-to-be-hipster-engineer</guid>

					<description>
                        <![CDATA[ In this article, I'm going to give you a slightly different view on]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>In this article, I&#8217;m going to give you a slightly different view on
something you do on a daily basis. A philosophy that encourages you to
only use what you really need, keeps you away from being distracted,
lets you think about what your problem really involves, and trust what
your fingers have learned instead of relying on visual representation.
That is what I call terminal-first mouseless development. I&#8217;ll
try to sell it to you by giving an overview of how you can benefit from it in
real life. Apart from the theory,
we are also going to see real applications of these approaches.
In particular, we will talk about <code>tmux</code> and <code>neovim</code> — industry standards for becoming a real
hipster developer.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_hipster_development">Hipster development?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We are all familiar with our IDEs, coupled with other tools for
tasks like database management (DBeaver), testing APIs (Postman), and
container management (Docker Desktop). Even though they provide an
extensive GUI, they are also quite rich in distracting elements.
Additionally, using multiple tools and navigating between them
means a lot of context switching as well as having quite a loaded
environment. If you say that your favorite IDE has everything
built-in, then it violates the philosophy of Unix, which says that it is
more idiomatic to use small tools that do one thing well rather than the
opposite. Sort of a single responsibility principle.</p>
</div>
<div class="paragraph">
<p>And here comes what I call &#8220;<strong>Terminal-first development</strong>&#8221;, but it can be
called by any other similar terms. Its core principle is that your terminal
should be your central hub for development tasks.
Instead of installing a new GUI application to solve a problem, the terminal-first
approach challenges you to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Decompose the problem into smaller, distinct steps.</p>
</li>
<li>
<p>Assign a small, dedicated CLI tool to each step.</p>
</li>
<li>
<p>Combine these tools, piping their outputs together, to solve the initial challenge.</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-07-11-terminal-first-mouseless-development-or-how-to-be-hipster-engineer/crazy-terminal.gif" alt="crazy terminal">
</div>
</div>
<div class="paragraph">
<p>As a practical example, you want to select an arbitrary table from your
database, get all of the data within it, and pretty-print it as JSON.
You have two options:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Install and use a couple hundred megabytes of &#8220;pgAdmin&#8221; that
will create another distractive window in your workflow and eat your
memory, blasting your brain with all the buttons and menus around its
GUI.</p>
</li>
<li>
<p>Create a simple script that uses &#8220;psql&#8221; to get a list of all
tables in your database, pass them to &#8220;fzf&#8221; so you could interactively
select them, pass the table name again to &#8220;psql&#8221; to output data from it in
JSON, and finally pass it to &#8220;jq&#8221; to pretty-print the result.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Even though I have already intentionally hated on option 1, there could
be a point like: &#8220;Why would I do everything said in point 2 if I
can just go to pgAdmin and press a single button or two?&#8221;. And that is
valid. And here we come to one of the most important points: the
approach described in option 2 is just an example of a philosophy that you
can follow or <strong>not</strong>. And that is your choice. And it would not be
incorrect or make you a bad developer. It is all about what you prefer
(and how lazy you are :P). But if you selected option 2, then you
probably prioritize:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Modularity &amp; Portability</p>
</li>
<li>
<p>Better resource usage</p>
</li>
<li>
<p>Minimalistic &amp; distraction-free workflow</p>
</li>
<li>
<p>Scriptability &amp; Automation</p>
</li>
<li>
<p>Customisation</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Another interesting thing to think about the second approach is that if
you look closely, it is all about piping, or basically passing
data from one function to another. And with this approach, it gives you a
grounded look at something fundamental regarding software engineering in
general — it is quite a lot, if not completely, about viewing,
manipulating, and creating data. Actually, pretty much what our brains do.</p>
</div>
<div class="paragraph">
<p>Last but not least — such an approach really encourages learning,
deeper understanding, and mastering of general software engineering
skills, which eventually raises the level of craftsmanship. Personally,
this philosophy is what sparks joy in my everyday work.</p>
</div>
<div class="paragraph">
<p>Another philosophy that usually goes hand in hand with the terminal-first
one is mouseless or keyboard-centric development, which literally means
what it says — it encourages you to prioritize the usage of a keyboard
over a mouse or trackpad for writing or navigating through your code.
It is important to say here that it doesn’t mean that you should never use
those. Sometimes it is quite inefficient to avoid using your trackpad,
but for the most part, the theory is that mostly using your keyboard
makes your development faster, more efficient, and less tiring.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-07-11-terminal-first-mouseless-development-or-how-to-be-hipster-engineer/monkey-smashing-mouse.gif" alt="monkey smashing mouse">
</div>
</div>
<div class="paragraph">
<p>Why? Well, you keep your hands on the keyboard and avoid switching
between it and a mouse. Additionally, you rely on muscle memory in the
form of keybindings and not on visual navigation to perform actions.
This way you reduce mental overhead, stay in the flow state, and perform basic
actions much faster. For example,
<a href="https://blog.superhuman.com/keyboard-vs-mouse/?utm_source=chatgpt.com">the
research by SuperHuman</a> showed that some basic operations that we
perform every day can be done from 2 to 5 seconds faster if performed
with a keyboard rather than a mouse. And we perform those actions a lot, so
think about the amount of time you could save in a day.</p>
</div>
<div class="paragraph">
<p>But let&#8217;s not idealize things and talk about the downsides. The primary one is a steep learning curve.
From my experience, following those philosophies really makes you
rethink the way you approach software development. I struggle to point
out the exact points, but it just feels quite different, and you really
need time to get used to that new reality, and that basically means a
long learning curve. The basic parts of it are memorizing all the
keybinds or switching your mindset to use CLIs over graphical applications. But
hey, learning all the buttons in your IDE also took time, so it is more
about whether you are ready to commit to that.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_core_in_practice">Core In Practice</h2>
<div class="sectionbody">
<div class="paragraph">
<p>So now, let’s finally go from something totally metaphorical to something
more practical. There isn&#8217;t a single right way to implement the philosophy that
I described above. But, while finding my own way there, I could
distinguish two core elements or, in fact, pieces of software, that will
help you to build up a foundation.</p>
</div>
<div class="sect2">
<h3 id="_tmux">tmux</h3>
<div class="paragraph">
<p>I mentioned that with terminal-first development, your terminal
becomes the &#8220;central hub&#8221; for solving software engineering tasks.
When you think about a hub, you probably expect it to provide an infrastructure
that you can utilize to effectively achieve your goals.
The industry standard for that is called &#8220;tmux&#8221;,
which is a terminal multiplexer by its definition.</p>
</div>
<div class="paragraph">
<p>It allows you to conveniently create terminal windows, splits, or even
sessions for grouping. That makes it easy to organize your work between
multiple projects, for example, and allows you to navigate more smoothly.</p>
</div>
<div class="paragraph">
<p>You can say, “Yeah, but my terminal emulator can do the same.” Sure, but
what if the keybinds change? What if you switch to another emulator? What if
you now have to use a different system? You basically need to adapt and
configure this new tool for yourself. So how does using &#8220;tmux&#8221; help you?
It is completely platform and terminal-emulator agnostic. Everything you have
to do is to put your configuration file in the root folder and run &#8220;tmux&#8221;.
This way you are completely independent of the platform that you run &#8220;tmux&#8221; on top of.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-07-11-terminal-first-mouseless-development-or-how-to-be-hipster-engineer/tmux.png" alt="tmux">
</div>
</div>
<div class="paragraph">
<p>Another thing is that, at its core, &#8220;tmux&#8221; is a server, having all
your terminal sessions working in the background. So this can at least save
you from accidentally pressing Command + Q in your terminal and crashing
out again, but what you can also do is to basically have your whole
terminal session setup running, that you can SSH from any other machine
and have it all there, as &#8220;tmux&#8221; itself is just a command-line tool.
Combining a configuration basis and server nature, you basically become
independent of a machine and/or terminal emulation tools, if you have
your &#8220;tmux&#8221; server hosted somewhere.</p>
</div>
<div class="paragraph">
<p>Regarding the config, you can do quite a lot, starting from setting basic keybinds,
finishing with writing custom scripts for your workflow or modifying the UI.
There is even a whole ecosystem of plugins!</p>
</div>
<div class="paragraph">
<p>And if we talk about downsides, there are not that many, except
for the steep learning curve, but trust me, the outcome is worth it.</p>
</div>
</div>
<div class="sect2">
<h3 id="_neovim">neovim</h3>
<div class="paragraph">
<p>I think most of you know &#8220;vi&#8221; — the editor that&#8217;s impossible to exit. One of the
first text editors in existence, it relies on the keyboard only.
This is because there was no mouse in the early computer days. Theoretically, you can use
it to perform any tasks related to text editing and code writing. But
the problem with the original &#8220;vi&#8221; is that it is as plain as possible,
and when it comes to modern development, not really efficient. For
example, not having the ability to autocomplete code or quickly navigate to
a class definition doesn’t sound like a lot of productivity.</p>
</div>
<div class="paragraph">
<p>To solve this issue, &#8220;vim&#8221; was created — a feature-rich version of
the original &#8220;vi&#8221; with things like syntax highlighting, the ability to split
windows, etc. And most importantly — it provides the ability for extensive
configuration, even featuring its own language — &#8220;vimscript&#8221;. That
basically created the possibility to write plugins that allow you to
customize your experience in &#8220;vim&#8221; however you want. As a result,
the &#8220;vim&#8221; plugin ecosystem is probably one of the biggest plugin
ecosystems in the world.</p>
</div>
<div class="paragraph">
<p>But this was not enough for people who considered themselves to be ultra-hipsters.
This led to the creation of &#8220;Neovim&#8221; - a fork, partly rewritten in Lua.
This way, significant gains were achieved in terms of extensibility and architecture.
Nowadays, &#8220;Neovim&#8221; is known for its great documentation and is supported by a quite big and active community of contributors.</p>
</div>
<div class="paragraph">
<p>Ultimately, you can think of &#8220;*vim&#8221; as a constructor.
Its ecosystem provides you with the bricks you can use to build a development
tool to satisfy any of your needs. From the most plain text editor to
an ultra-feature-rich IDE. Basically, you can completely replace whatever you are using now.
Just watch out so as not to violate the Unix philosophy.</p>
</div>
<div class="paragraph">
<p>So how does switching to &#8220;*vim&#8221; feel, and what does it bring to your
life? First of all, text editing starts to feel so much smoother, and the
whole navigation process around the code feels really fluent. Using
&#8220;*vim&#8221; really proves the benefits of trusting your muscle memory via
keybinds instead of visual navigating. The overall overhead goes down,
and you can also feel it when you have to work with several
projects/directories. Opening a project, quickly looking for something,
and editing it feels so light and easy. Using &#8220;*vim&#8221; is like dropping
a huge backpack when going uphill and changing it for something small,
compact, accessible, but extendable at the same time. And last but not
least, making the editor behave literally however you want it to in
a programmatic way is another amazing part.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-07-11-terminal-first-mouseless-development-or-how-to-be-hipster-engineer/nvim.png" alt="nvim">
</div>
</div>
<div class="paragraph">
<p>But let’s not forget about the struggles you may face: &#8220;*vim&#8221; really makes
you rethink the way you write your code (and using keybinds is not the
only part), which will take quite some time. Another thing is
configuring the thing to meet your needs. Yeah, that takes time.
Initially, it took me maybe like 20+ hours, and it is also a non-stop
process, but that is a fair trade-off for the extensibility you get. There
is a joke in the &#8220;*vim&#8221; community about people spending more time on customizing
their config than on actually using it. And another thing is
that as it is community-driven, you may face things that don’t work
properly. For example, in order to have all the IDE features for Java,
you need to run Eclipse’s &#8220;jdtls&#8221;, a language server,
which doesn&#8217;t usually perform well on a large Java codebase.
But your mileage may vary.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusions">Conclusions</h2>
<div class="sectionbody">
<div class="paragraph">
<p>My main point in this article was to provide you with a new perspective.
An approach that you can incorporate in your day-to-day tasks.
A philosophy that embraces you to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Think more about what your task involves and what you really need to solve it</p>
</li>
<li>
<p>Maintain your workspace clean and distraction-free</p>
</li>
<li>
<p>Build a unique environment that you love working in</p>
</li>
<li>
<p>Introduce joy and creativity into your routine</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>And while it might feel like a step back, this philosophy is surprisingly forward-thinking.
Many of today&#8217;s brand-new AI tools are designed specifically for the command line.
In the end, there is no single way to do things.
Technical benefits like efficiency are important, but so is finding joy and pride in your craft.
Hipster engineering is something that makes me a better professional and makes me love what I do.
My sincere hope is that you find your own way to do the same.
Thanks for reading.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-07-11-terminal-first-mouseless-development-or-how-to-be-hipster-engineer/dancing-puppy.gif" alt="dancing puppy">
</div>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Ctrl+Alt+Defeat: Noob vs. Neural Net</title>
		<link>https://blog.lunatech.com//posts/2025-07-04-ctrl-alt-defeat</link>
		
		<dc:creator><![CDATA[]]></dc:creator>
        <pubDate>2025-07-04T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[beyond-the-code]]></category>
                }
             {
            <category><![CDATA[bash-to-the-feature]]></category>
                }
             {
            <category><![CDATA[AI]]></category>
                }
             {
            <category><![CDATA[neural network]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-07-04-ctrl-alt-defeat</guid>

					<description>
                        <![CDATA[ For decades, competitive games have served as milestones in artificial intelligence research. From IBM’s Deep Blue beating Garry Kasparov at chess in 1997, to AlphaGo’s victory over Lee Sedol in 2016, games have offered a clear stage for AI to measure itself against the best human minds.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>For decades, competitive games have served as milestones in artificial intelligence research. From IBM’s Deep Blue beating Garry Kasparov at chess in 1997, to AlphaGo’s victory over Lee Sedol in 2016, games have offered a clear stage for AI to measure itself against the best human minds.</p>
</div>
<div class="paragraph">
<p>But as games have grown more complex, so too has the challenge. Turn-based board games like chess and Go, while difficult, offer complete information and relatively limited options per move. Real-time strategy games like StarCraft II and Dota 2, however, introduce chaos: thousands of actions per minute, imperfect information, shifting alliances, and the need for long-term planning — all in real time.</p>
</div>
<div class="paragraph">
<p>So the question arises: Can AI beat humans in these games, fairly, without relying on superhuman speed or godlike awareness? Let’s take a deeper look into how two landmark systems, OpenAI Five and AlphaStar, set out to do just that.<br></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_complexity_of_real_time_strategy_games">The Complexity of Real-Time Strategy Games</h2>
<div class="sectionbody">
<div class="paragraph">
<p>To appreciate the achievements of AI in games like Dota 2 and StarCraft II, it&#8217;s essential to understand what makes these games hard:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Partial information: Players don’t see the whole map due to fog of war.<br></p>
</li>
<li>
<p>High action complexity: At any moment, there are thousands of possible moves.<br></p>
</li>
<li>
<p>Real-time dynamics: No turns, decisions must be made continuously.<br></p>
</li>
<li>
<p>Coordination: Especially in team games, success hinges on synergy and communication.<br></p>
</li>
<li>
<p>Long-term planning: Decisions made in the early game can determine the late-game outcome.<br></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Unlike games with set openings and established endgames, these environments are closer to the messiness of the real world and that’s exactly why they interest AI researchers.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_openai_five_dota_2_without_superpowers">OpenAI Five: Dota 2 Without Superpowers</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In 2019, OpenAI introduced OpenAI Five, a team of five neural networks trained to play Dota 2, a popular and highly complex team-based multiplayer game. Unlike previous AIs, OpenAI Five didn’t rely on hardcoded rules. Instead, it learned by playing itself millions of times in a massive distributed training setup.</p>
</div>
<div class="paragraph">
<p>What made OpenAI Five especially impressive was the effort to simulate human-like constraints:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Reaction time was capped at 200 milliseconds, close to average human reflexes.<br></p>
</li>
<li>
<p>Actions per minute (APM) were restricted to human levels, avoiding the inhuman speed advantage.<br></p>
</li>
<li>
<p>It had no access to information unavailable to humans, like opponent positions under fog of war.<br></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Despite these constraints, OpenAI Five steadily improved and eventually beat top human teams culminating in a 2–0 victory over the reigning world champion team OG at The International in 2019.</p>
</div>
<div class="paragraph">
<p>Yet, Five was not without flaws. It was:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Weaker in novel situations it hadn’t seen during training.<br></p>
</li>
<li>
<p>Sometimes inflexible, making odd decisions when opponents deviated from expected tactics.<br></p>
</li>
<li>
<p>Emotionally agnostic, meaning while it would never play emotionally, it also doesn’t have the ability to read human psychology or use momentum the way humans do.<br></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Still, the fact that an AI could beat the world’s best without using non-human like reactions speeds speaks for itself.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_alphastar_outmaneuvering_pros_in_starcraft_ii">AlphaStar: Outmaneuvering Pros in StarCraft II</h2>
<div class="sectionbody">
<div class="paragraph">
<p>DeepMind’s AlphaStar tackled StarCraft II, another legendary RTS known for its brutal learning curve and mechanical demands. It used a combination of imitation learning (watching human games) and self-play reinforcement learning to master the game.</p>
</div>
<div class="paragraph">
<p>Like OpenAI Five, AlphaStar imposed human-like limitations:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Capped APM and reaction delays (averaging around 350 milliseconds).<br></p>
</li>
<li>
<p>Camera view limitations, meaning it had to "look" around the map like a human player.<br></p>
</li>
<li>
<p>Trained against a diversity of opponents and strategies.<br></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>AlphaStar climbed the European ladder to Grandmaster, ranking in the top 0.2% of players. It beat professional players like TLO and MaNa convincingly, sometimes using unexpected and creative strategies.</p>
</div>
<div class="paragraph">
<p>However, it too had its shortcomings:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Predictability: Once players had time to study it, some human pros found exploitable patterns.<br></p>
</li>
<li>
<p>Lack of intuitive game sense: Humans often make intuitive calls based on experience, psychology, or a “feel” for the flow of the game — AlphaStar relied solely on data and outcomes.<br></p>
</li>
<li>
<p>Difficulty adapting to “meta shifts”, since it lacked the kind of quick generalization humans can make from limited examples.<br></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Even so, AlphaStar showed that AI could compete — and win — not by outclicking, but by outthinking.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_summary_of_the_restrictions_imposed_on_the_ais">Summary of the restrictions imposed on the AI&#8217;s</h2>
<div class="sectionbody">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Metric</th>
<th class="tableblock halign-left valign-top">Pro Human Player</th>
<th class="tableblock halign-left valign-top">OpenAI Five</th>
<th class="tableblock halign-left valign-top">AlphaStar</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Average Reaction Time</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">~200 ms</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">200 ms</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">350 ms</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Max APM</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">~300</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">~180</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">~150</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Map Vision</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Partial</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Partial</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Limited camera view</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Strategy Adaptation</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">High</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Medium</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Medium-High</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect1">
<h2 id="_where_ai_still_falls_short">Where AI Still Falls Short</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Even with these victories, AI is not yet a complete replacement for human intelligence in games. Some weaknesses include:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Lack of common sense or intuition: AIs still struggle with decisions that require real-world reasoning or emotional awareness.<br></p>
</li>
<li>
<p>Context blindness: Without extensive training, AIs can’t generalize well to new game versions or unexpected strategies.<br></p>
</li>
<li>
<p>Communication and deception: While some advanced AIs, like Meta&#8217;s "CICERO", have made progress in games that involve negotiation and persuasion — such as the board game Diplomacy — most game-playing AIs still struggle to understand or influence human opponents the way skilled players can. They can&#8217;t read body language, sense bluffing, or adapt their strategy based on trust or psychology — key elements of human gameplay.<br></p>
</li>
<li>
<p>Creativity with purpose: AIs can discover new tactics, but they don’t “understand” them in a human sense, nor can they justify them conceptually.<br></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In essence, AI can win — but it doesn&#8217;t always know why it wins.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion_a_new_era_of_competitive_play">Conclusion: A New Era of Competitive Play</h2>
<div class="sectionbody">
<div class="paragraph">
<p>So, can AI truly outplay humans at complex games? The answer, remarkably, is yes — even under constraints that mimic human limitations. OpenAI Five and AlphaStar both demonstrated that intelligent agents can excel in games long thought too complex for machines.</p>
</div>
<div class="paragraph">
<p>But while the victories are real, the limitations are too. These AIs don’t think or feel like us. They win through scale, training, and narrow focus — not general intelligence or intuition.</p>
</div>
<div class="paragraph">
<p>Still, each success in these arenas nudges us closer to AI that can not only perform, but reason, adapt, and collaborate — skills that matter far beyond the game board.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>GPU Programming For The Brave</title>
		<link>https://blog.lunatech.com//posts/2025-06-27-gpu-programming-for-the-brave</link>
		
		<dc:creator><![CDATA[Boyuan Xiao]]></dc:creator>
        <pubDate>2025-06-27T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[beyond-the-code]]></category>
                }
             {
            <category><![CDATA[bash-to-the-feature]]></category>
                }
             {
            <category><![CDATA[GPU programming]]></category>
                }
             {
            <category><![CDATA[CUDA]]></category>
                }
             {
            <category><![CDATA[parallel programming]]></category>
                }
             {
            <category><![CDATA[AI]]></category>
                }
             {
            <category><![CDATA[neural network]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-06-27-gpu-programming-for-the-brave</guid>

					<description>
                        <![CDATA[ GPUs, some might call them graphic cards, have never been a stranger for video gamers. The evolution of GPUs significantly changed not just the video game industry, but also the field of parallel programming.]]></description>
                    <content:encoded><![CDATA[
                    <div class="sect1">
<h2 id="_introduction">Introduction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>GPUs, some might call them graphic cards, have never been a stranger for video gamers. The evolution of GPUs significantly changed not just the video game industry, but also the field of parallel programming.</p>
</div>
<div class="paragraph">
<p>I was lucky enough to participate some courses that briefly introduced GPU programming during my master study. As my first humble attempt, I hereby write down my knowledge and understanding about GPU programming in this blog post. Hopefully, after reading this write-up, you can have a basic idea about: how GPUs work, how to do some simple GPU programming and why it is so important to the field of AI.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_is_gpu">What is GPU</h2>
<div class="sectionbody">
<div class="paragraph">
<p>GPU is the abbreviation of "graphics processing unit". It is a specialized electronic circuit designed for digital image processing and to accelerate computer graphics, being present either as a discrete video card or embedded on motherboards, mobile phones, personal computers, workstations, and game consoles.</p>
</div>
<div class="paragraph">
<p>If you have tried to build your own PC, you will probably call the big gas-stove-look-a-like thing in <a href="#card">Figure 1</a> a GPU. However, this is not entirely correct. Graphics card is a more suitable name for it. And if you have ever had a chance to disassemble a graphics card like me, then I am sure you will notice there are much more than just a GPU on a graphics card. The GPU itself is only a small part on it and there&#8217;s memory, power supply and cooling unit. The composition resembles any normal PC you can see. <a href="#disassembled-card">Figure 2</a> is taken when I had an GPU memory overheating issue. As you can see in the picture, the GPU is surrounded by the red box and blue boxes for the GPU memory. The part on the right is the cooling unit.</p>
</div>
<div class="openblock float-group">
<div class="content">
<div id="card" class="imageblock left">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/my_graphics_card_2.png" alt="A" width="800">
</div>
<div class="title">Figure 1. My Nvidia RTX 3080.</div>
</div>
<div id="disassembled-card" class="imageblock right">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/my_graphics_card_1.png" alt="B" width="800">
</div>
<div class="title">Figure 2. My Nvidia RTX 3080 (disassemble).</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_small_exercise">The small exercise</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There is no way we can actually know how to do GPU programming by just looking at the composition picture. To help with the understanding, let&#8217;s consider a small code exercise where you have to implement a simple <code>fill_matrix</code> function in C to fill a rectangle shape within a two-dimensional matrix with some certain value:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-c" data-lang="c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

void fill_matrix(int **s, int x_len, int y_len, int draw_start, int draw_end, int value_to_fill) {
  // IMPLEMENT ME
}

void print_matrix(int **s, int x_len, int y_len) {
  // doesn't matter...
}

int64_t initialize_matrix(int rows, int cols) {
  // doesn't matter...
}

int main() {
  int **s = (int **)initialize_matrix(10, 10);
  printf("before: \n");
  print_matrix(s, 10, 10);

  fill_matrix(s, 10, 10, 2, 8 9);

  printf("after: \n");
  print_matrix(s, 10, 10);
  return 0;
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_c_version">C version</h3>
<div class="paragraph">
<p>Easy, isn&#8217;t it? All we need to do is to use two nested for-loops to fill the value when the loop arrives the expected range:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-c" data-lang="c">void fill_matrix(int **s, int x_len, int y_len, int draw_start, int draw_end, int value_to_fill) {
  for (int i = 0; i &lt; y_len; i++) {
    for (int j = 0; j &lt; x_len; j++) {
      if (i &gt; draw_start &amp;&amp; i &lt; draw_end &amp;&amp; j &gt; draw_start &amp;&amp; j &lt; draw_end) {
        s[i][j] = value_to_fill;
      }
    }
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you would like to speed up your implementation, you can even use <a href="https://www.openmp.org/">OpenMp</a> to turn your it into a multi-threaded implementation by simply adding <code>#pragma omp parallel for collapse(2)</code> on top of the outer for-loop. After re-compiling and running <code>export OMP_NUM_THREADS=4</code>, your program should automatically delegate the execution of the for-loop to at most 4 threads.</p>
</div>
<div class="paragraph">
<p>Now it seems like we really pushed to the boundary, and couldn&#8217;t get any more speedup unless increasing the number of threads. However, what we have seen so far is still in the realm of CPU programming, where your code gets executed by the CPU. Besides that, the time complexity of the implementation is <code>O(n*m)</code>, which is not a very pleasant number. So let&#8217;s try to make use of the power of GPUs, with which we could achieve O(1) complexity.</p>
</div>
</div>
<div class="sect2">
<h3 id="_cuda_version">CUDA version</h3>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">Tip</div>
</td>
<td class="content">
<div class="paragraph">
<p>You might find it helpful to temporarily forget what you have learnt about <code>thread</code> and <code>kernel</code> when reading this section.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-c" data-lang="c">__global__ void fill_matrix_kernel(int* matrix, int rows, int cols, int draw_start, int draw_end, int value) {
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;

    if (row &gt; draw_start &amp;&amp; row &lt; draw_end &amp;&amp; col &lt; draw_end &amp;&amp; col &gt; draw_start) {
        int idx = row * cols + col;
        matrix[idx] = value;
    }
}

int main() {
  const int rows = 10;
  const int cols = 10;
  const size_t size = rows * cols * sizeof(int);

  // Host memory
  int* h_matrix = (int *)malloc(size);

  // Device memory
  int* d_matrix;
  cudaMalloc((void **)&amp;d_matrix, size);

  // Define grid and block dimensions
  dim3 block(32, 32);  // 256 threads per block
  dim3 grid(
      (cols + block.x - 1) / block.x,  // ceil(cols/block.x)
      (rows + block.y - 1) / block.y   // ceil(rows/block.y)
  );

  // Launch kernel
  fill_matrix_kernel&lt;&lt;&lt;grid, block&gt;&gt;&gt;(d_matrix, rows, cols, 2, 8 9);

  // Copy result back to host
  cudaMemcpy(h_matrix, d_matrix, size, cudaMemcpyDeviceToHost);

  // Verify values
  print_matrix(h_matrix, rows, cols);

  // Cleanup
  free(h_matrix);
  cudaFree(d_matrix);

  return 0;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Above is the CUDA implementation. CUDA is a C-like programming language provided by Nvidia. Naturally, it only runs on Nvidia cards. To compile the code above, we can simply run <code>nvcc -o code_example code_example.cu</code> just like when compiling C code using <code>gcc</code>. Then, the <code>code_example</code> it produces also isn&#8217;t any different from other native executable, which could be run by the command <code>./code_example</code>.</p>
</div>
<div class="paragraph">
<p>So what happens when we run it? Besides allocating memory on the device, which is our graphics card, the computation kernel (the <code>fill_matrix_kernel</code> function) is executed by all the GPU threads that we requested simultaneously. In the example, we define a grid of one block ((10 + 32 - 1) / 32 = 1) with 256 threads on it. GPU threads are fundamentally different from the CPU threads we know. By design, the number of GPU threads on a GPU is much larger than the number of CPU threads on a CPU. On top of that, what is executed by CPU threads is completely dependent on how you program it. On contrast, GPU threads provides high-throughput due to the nature of simultaneous execution for a kernel. Therefore, we need a way to control the behavior of each GPU thread. Luckily, an unique <code>threadId</code> is assigned to each GPU thread within the same block and each block has a unique <code>blockId</code>. What we can do is to see if the the current thread is within the drawing range and fill the value accordingly based on the location of the thread (<code>blockId</code> * <code>number of blocks</code> + <code>threadId</code>) , which is exactly what the <code>if</code> clause is doing.</p>
</div>
<div class="openblock float-group">
<div class="content">
<div class="imageblock left">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/gpu_architecture_1.png" alt="A" width="480">
</div>
<div class="title">Figure 3. The Grid, The Block and The Thread.</div>
</div>
<div class="imageblock right">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/gpu_architecture_2.png" alt="B" width="800">
</div>
<div class="title">Figure 4. Nvidia&#8217;s interpretation.</div>
</div>
</div>
</div>
<div class="paragraph">
<p>If we leave out the <code>main()</code> function, the actual implementation is only 6 lines and there is no loop being used at all. But how much faster it really is? When running with the matrix shape of 32768 * 32768, our CUDA implementation can finish it within 0.3 seconds while the C implementation needs 1.9 seconds.</p>
</div>
<div class="openblock float-group">
<div class="content">
<div class="imageblock text-center">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/speed_result.png" alt="Speed showcase." width="800">
</div>
<div class="title">Figure 5. Speed showcase.</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Impressive, isn&#8217;t it? But trust me, everything seems reasonable when you actually see the difference of thread numbers:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top"><a href="https://www.intel.com/content/www/us/en/products/sku/240782/intel-xeon-6966pc-processor-432m-cache-3-00-ghz/specifications.html">Intel® Xeon® 6966P-C Processor</a></th>
<th class="tableblock halign-left valign-top"><a href="https://www.amd.com/en/products/processors/workstations/ryzen-threadripper.html#specifications">AMD Ryzen Threadripper PRO 9995WX</a></th>
<th class="tableblock halign-left valign-top"><a href="https://www.nvidia.com/en-us/geforce/graphics-cards/50-series/rtx-5090/">Nvidia RTX 5090</a></th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">192 threads in total</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">192 threads in total</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">21760 CUDA threads</p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">Tip</div>
</td>
<td class="content">
<div class="paragraph">
<p>This code example might be too simple and too boring for you. But if you think of the matrix that we are filling as a screen, and the value as RGB value&#8201;&#8212;&#8201;We are actually rendering a screen!</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_okay_but_why_ai">Okay, but why AI?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>As you might have heard, GPUs are widely used in the field of AI. Given the high-throughput trait of GPU, the process of AI model training can be significantly facilitated. But why is that?</p>
</div>
<div class="sect2">
<h3 id="_look_into_the_ai">Look into the AI</h3>
<div class="paragraph">
<p>Thankfully, Wikipedia made it a lot easier for me to explain AI:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>"The largest and most capable LLMs are generative pretrained transformers (GPTs), which are largely used in generative chatbots such as ChatGPT or Gemini."</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>"A GPT is a type of LLM and a prominent framework for generative artificial intelligence. It is an artificial neural network that is used in natural language processing by machines.".</pre>
</div>
</div>
<div class="paragraph">
<p>To put it simply: <strong>most of the popular AIs are made of neural networks.</strong> A neural network is composite of multiple layers of nodes. The first layer takes input from the outside world, normally as the format of a vector of numbers. The output of a layer consists of the output number from each node, which is calculated by summing the input times the weight of the node (sum(input * weight)). And all the subsequent layers take input from the previous one. The process of training the neural network aims to find the weights for each nodes so that the output is most acceptable. And it requires to feed the input &#8594; calculate the output &#8594; compare with the expected output &#8594; adjust the weights repetitively.</p>
</div>
<div class="openblock float-group">
<div class="content">
<div class="imageblock text-center">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/neural_network.png" alt="Speed showcase." width="800">
</div>
<div class="title">Figure 6. A neural network.</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_we_can_let_gpu_run_this">We can let GPU run this</h3>
<div class="paragraph">
<p>If we try to write a simple implementation, or even pseudo code, of how things are done in each layer of a neural network, we could arrive at what is shown in <a href="#c-implementation">Figure 7</a>. Once again we see a pattern we have seen just before: a linear algebra calculation wrapped by two for-loops. Therefore, we can easily rewrite to a CUDA implementation shown in <a href="#cuda-implementation">Figure 8</a>.</p>
</div>
<div class="openblock float-group">
<div class="content">
<div id="c-implementation" class="imageblock left">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/neural_network_layer_c.png" alt="Speed showcase." width="600">
</div>
<div class="title">Figure 7. C implementation of a neural network layer.</div>
</div>
<div id="cuda-implementation" class="imageblock right">
<div class="content">
<img src="../media/2025-06-27-gpu-programming-for-the-brave/neural_network_layer_cuda.png" alt="Speed showcase." width="900">
</div>
<div class="title">Figure 8. CUDA implementation of a neural network layer.</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Both <a href="#c-implementation">Figure 7</a> and <a href="#cuda-implementation">Figure 8</a> are taken from a research article by Ricardo Brito et al[<a href="#1">[1]</a>]. The authors managed to utilize the high-throughput of GPU to accelerate the training process of a neural network in the year of 2016. Except for the countless open-source repositories that implement CUDA-based neural networks, Nvidia offers <a href="https://developer.nvidia.com/cudnn#">cuDNN</a> as a GPU-accelerated library of primitives for deep neural networks. Popular neural network frameworks like <a href="https://pytorch.org/get-started/locally/">PyTorch</a> and <a href="https://www.tensorflow.org/guide/gpu">TenhsorFlow</a> can operate on GPU devices without any extra effort.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_to_sum_up">To sum up</h2>
<div class="sectionbody">
<div class="paragraph">
<p>GPUs, which are originally made for graphics processing, has shown a huge potential in the field of parallel programming and AI training due to their high-throughput nature. This is achieved by piling significant amount of GPU threads and impose simultaneous execution of the compute kernel. Even though it&#8217;s not quite possible to assign GPU threads for different execution routine like CPU threads, we can still do minimum control-flow manipulation based on their <code>threadId</code>. Several examples of CUDA, which is a C-like GPU programming language offered by Nvidia, are also shown to demonstrate its syntax.</p>
</div>
<div class="paragraph">
<p>Finally, you can checkout the code examples I used in <a href="https://github.com/555isfaiz/gpu_programming_example">this GitHub repo</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_references">References</h2>
<div class="sectionbody">
<div class="ulist bibliography">
<ul class="bibliography">
<li>
<p>[[[1]]] Brito R., Fong S., Cho K., Song W., Wong R., Mohammed S., Fiaidhi J.
"GPU-enabled back-propagation artificial neural network for digit recognition in parallel".
<em>The Journal of Supercomputing</em>. 72, (2016).
<a href="https://doi.org/10.1007/s11227-016-1633-y" class="bare">https://doi.org/10.1007/s11227-016-1633-y</a></p>
</li>
</ul>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Graveyard of technologies</title>
		<link>https://blog.lunatech.com//posts/2025-06-20-graveyard-of-technologies</link>
		
		<dc:creator><![CDATA[Pere Tarrida]]></dc:creator>
        <pubDate>2025-06-20T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[beyond-the-code]]></category>
                }
             {
            <category><![CDATA[bash-to-the-feature]]></category>
                }
             {
            <category><![CDATA[ALGOL]]></category>
                }
             {
            <category><![CDATA[Japronto]]></category>
                }
             {
            <category><![CDATA[Apache Ant]]></category>
                }
             {
            <category><![CDATA[Windows Phone]]></category>
                }
             {
            <category><![CDATA[Google Glass]]></category>
                }
             {
            <category><![CDATA[Microsoft Zune]]></category>
                }
             {
            <category><![CDATA[Netscape Navigator]]></category>
                }
             {
            <category><![CDATA[Adobe Flash]]></category>
                }
             {
            <category><![CDATA[Visual Basic]]></category>
                }
             {
            <category><![CDATA[Fortran]]></category>
                }
             {
            <category><![CDATA[Smalltalk]]></category>
                }
             {
            <category><![CDATA[Technology Adoption]]></category>
                }
             {
            <category><![CDATA[Market Failure]]></category>
                }
             {
            <category><![CDATA[Software History]]></category>
                }
             {
            <category><![CDATA[Hardware History]]></category>
                }
             {
            <category><![CDATA[Ecosystem Strategy]]></category>
                }
             {
            <category><![CDATA[Innovation vs. Viability]]></category>
                }
             {
            <category><![CDATA[Legacy Technology]]></category>
                }
             {
            <category><![CDATA[Product Lifecycle]]></category>
                }
             {
            <category><![CDATA[Developer Experience]]></category>
                }
             {
            <category><![CDATA[User Adoption]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-06-20-graveyard-of-technologies</guid>

					<description>
                        <![CDATA[ ALGOL, Japronto, and Apache Ant represent fascinating case studies of software that, despite their technical merits, faced significant challenges in achieving lasting market success. Each offers valuable lessons about the complex relationship between technical excellence and commercial viability.]]></description>
                    <content:encoded><![CDATA[
                    <div class="sect1">
<h2 id="_software_when_technical_excellence_isnt_enough">Software: When Technical Excellence Isn&#8217;t Enough</h2>
<div class="sectionbody">
<div class="paragraph">
<p>ALGOL, Japronto, and Apache Ant represent fascinating case studies of software that, despite their technical merits, faced significant challenges in achieving lasting market success. Each offers valuable lessons about the complex relationship between technical excellence and commercial viability.
 ALGOL, Japronto, and Apache Ant: Lessons in Technology Adoption</p>
</div>
<div class="paragraph">
<p>Represent fascinating case studies of technologies that, despite their technical merits, faced significant challenges in achieving lasting market success. Each offers valuable lessons about the complex relationship between technical excellence and commercial viability.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_algol_revolutionary_syntax_that_shaped_programming">ALGOL: Revolutionary Syntax That Shaped Programming</h2>
<div class="sectionbody">
<div class="paragraph">
<p>ALGOL introduced groundbreaking programming concepts that influenced virtually every modern programming language. The language featured elegant recursive procedures and call-by-name parameters, as demonstrated in this classic example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-algol" data-lang="algol">real procedure GPS (I, N, Z, V); integer I; real N, Z, V;
begin
   for I := 1 step 1 until N do
      Z := V;
   GPS := 1;
end;</code></pre>
</div>
</div>
<div class="paragraph">
<p>This GPS (General Problem Solver) procedure showcased ALGOL&#8217;s sophisticated parameter passing mechanisms and recursive capabilities. The language&#8217;s clean syntax and mathematical precision made it ideal for academic research and algorithm description. ALGOL&#8217;s influence can be seen in modern languages through its introduction of block structure, lexical scoping, and formal syntax definition using Backus-Naur Form.</p>
</div>
<div class="paragraph">
<p>However, ALGOL&#8217;s academic origins became its commercial weakness. The language prioritized theoretical elegance over practical business applications, creating a barrier for commercial adoption. Unlike FORTRAN, which had IBM&#8217;s backing and clear scientific computing applications, ALGOL remained primarily an academic exercise without strong industry support.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_japronto_the_performance_paradox">Japronto: The Performance Paradox</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Japronto emerged as a high-performance Python HTTP framework, promising extraordinary speed through aggressive optimization. A typical Japronto application demonstrated its streamlined approach:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-python" data-lang="python">from japronto import Application

def hello(request):
    return request.Response(text='Hello world!')

app = Application()
app.router.add_route('/', hello)
app.run(debug=True)</code></pre>
</div>
</div>
<div class="paragraph">
<p>The framework achieved remarkable performance by optimizing HTTP pipelining and utilizing the picohttpparser C library with SSE4.2 CPU instructions. Japronto could handle over 1 million requests per second in benchmarks, dramatically outperforming traditional Python frameworks and even some Go alternatives.</p>
</div>
<div class="paragraph">
<p>However, this performance came at a significant cost. Japronto&#8217;s speed optimizations discouraged the use of Python-level objects and complex application logic. The framework relied heavily on HTTP pipelining, which modern browsers don&#8217;t support reliably. Most critically, real-world applications requiring database connections, business logic, and data processing couldn&#8217;t maintain Japronto&#8217;s benchmark performance levels.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_apache_ant_the_xml_build_tool_era">Apache Ant: The XML Build Tool Era</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Apache Ant dominated Java build automation for years with its XML-based configuration system. A typical Ant build file demonstrated its declarative approach:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;project name="MyFirstAntProject" default="compile" basedir="."&gt;
    &lt;property name="src.dir" location="src" /&gt;
    &lt;property name="build.dir" location="bin" /&gt;

    &lt;target name="clean"&gt;
        &lt;delete dir="${build.dir}" /&gt;
    &lt;/target&gt;

    &lt;target name="compile" depends="clean"&gt;
        &lt;javac srcdir="${src.dir}" destdir="${build.dir}" /&gt;
    &lt;/target&gt;
&lt;/project&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Ant succeeded in providing platform-independent build automation and flexible task execution. Its XML-based configuration allowed detailed control over build processes, and its extensible architecture supported custom tasks and complex build workflows.</p>
</div>
<div class="paragraph">
<p>However, Ant&#8217;s imperative approach became increasingly cumbersome as projects grew in complexity. The lack of dependency management, standardized project layouts, and convention-over-configuration principles made Ant builds verbose and difficult to maintain. Maven&#8217;s introduction of automatic dependency resolution and Gradle&#8217;s programmatic build scripts eventually displaced Ant in most modern Java projects.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_netscape_navigator_the_pioneer_that_lost_the_war">Netscape Navigator: The Pioneer That Lost the War</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Netscape Navigator pioneered web browsing and helped create the modern internet, but ultimately fell victim to Microsoft&#8217;s aggressive competitive tactics and strategic missteps. Despite its early dominance, Netscape&#8217;s market share collapsed in the late 1990s.</p>
</div>
<div class="paragraph">
<p>Netscape Navigator introduced fundamental web technologies including JavaScript, SSL encryption, and dynamic HTML capabilities. The browser&#8217;s plugin architecture and standards-based approach laid the foundation for modern web development and demonstrated the potential for rich, interactive web applications.</p>
</div>
<div class="paragraph">
<p>However, Netscape&#8217;s downfall illustrates how market leadership can quickly evaporate in fast-moving technology sectors. Microsoft leveraged its Windows monopoly to bundle Internet Explorer with every PC, making it the default browser for millions of users. Netscape also made strategic errors, including focusing too heavily on enterprise solutions while neglecting the consumer market, allowing Microsoft to catch up and eventually surpass Netscape&#8217;s technical capabilities.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_adobe_flash_actionscript_a_multimedia_giants_swift_decline">Adobe Flash + ActionScript: A Multimedia Giant&#8217;s Swift Decline</h2>
<div class="sectionbody">
<div class="paragraph">
<p>For over a decade, Adobe Flash was the dominant platform for rich multimedia content on the web. Powered by ActionScript, a scripting language similar to JavaScript, Flash enabled highly interactive websites, games, and animations. A classic snippet of ActionScript might look like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-actionscript" data-lang="actionscript">var myText:TextField = new TextField();
myText.text = "Hello, World!";
myText.x = 100;
myText.y = 100;
addChild(myText);</code></pre>
</div>
</div>
<div class="paragraph">
<p>This interactivity revolutionized early web experiences, making Flash a staple in web development and advertising. ActionScript 3.0 introduced object-oriented features, bringing more structure and power to web applications.</p>
</div>
<div class="paragraph">
<p>Despite its popularity, Flash faced mounting criticism for its performance, security vulnerabilities, and closed ecosystem. The death knell came when Apple declined to support Flash on iOS, signaling the industry&#8217;s shift toward open standards like HTML5, CSS3, and JavaScript. Adobe officially ended Flash support in 2020.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_visual_basic_accessibility_meets_obsolescence">Visual Basic: Accessibility Meets Obsolescence</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Microsoft’s Visual Basic (VB) democratized Windows software development in the 1990s. Its simple syntax and drag-and-drop interface allowed non-programmers and beginners to create full-fledged Windows applications rapidly:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-vb" data-lang="vb">Module Module1
   Sub Main()
     Console.WriteLine("Hello World!")
   End Sub
End Module</code></pre>
</div>
</div>
<div class="paragraph">
<p>VB&#8217;s tight integration with the Windows API and rapid application development tools made it a hit for business applications. However, as .NET and more modern languages like C# emerged, Visual Basic was gradually phased out. VB.NET, its successor, attempted modernization but lacked traction among new developers.</p>
</div>
<div class="paragraph">
<p>The rise of more versatile, cross-platform, and open-source development frameworks ultimately made Visual Basic an outdated choice for contemporary software needs.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_fortran_the_long_reigning_king_of_scientific_computing">Fortran: The Long-Reigning King of Scientific Computing</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Developed in the 1950s, Fortran (FORmula TRANslation) was one of the first high-level programming languages. Its strength in numerical computation made it the go-to choice for scientists and engineers for decades. A basic Fortran program might look like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-fortran" data-lang="fortran">PROGRAM Hello
   PRINT *, 'Hello, world!'
END PROGRAM Hello</code></pre>
</div>
</div>
<div class="paragraph">
<p>Fortran introduced critical concepts like structured programming and efficient array handling, which made it ideal for high-performance computing tasks such as climate modeling and computational fluid dynamics.</p>
</div>
<div class="paragraph">
<p>Though still used in legacy scientific codebases, Fortran&#8217;s relevance has dwindled. Modern languages like Python, with libraries like NumPy and SciPy, offer more flexible and accessible alternatives, leading to Fortran’s slow fade from the mainstream.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_smalltalk_object_oriented_pioneer_with_limited_reach">Smalltalk: Object-Oriented Pioneer with Limited Reach</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Smalltalk was a trailblazer in object-oriented programming. It introduced core concepts such as message passing, live coding environments, and a uniform object model that inspired languages like Java, Python, and Ruby. A Smalltalk code example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-smalltalk" data-lang="smalltalk">Transcript show: 'Hello, world!'; cr.</code></pre>
</div>
</div>
<div class="paragraph">
<p>The entire environment was built from objects, offering unprecedented dynamism and introspection. Smalltalk&#8217;s interactive IDE and immediate feedback loop remain unmatched in some respects.</p>
</div>
<div class="paragraph">
<p>Yet, Smalltalk struggled with adoption due to performance issues, steep learning curves, and limited tooling outside its own ecosystem. While it remains influential in academic and niche circles, it was overshadowed by more pragmatic object-oriented languages that better integrated with mainstream operating systems and development workflows.</p>
</div>
</div>
</div>
<h1 id="_hardware_nightmares_when_innovation_meets_market_reality" class="sect0">Hardware Nightmares: When Innovation Meets Market Reality</h1>
<div class="paragraph">
<p>Windows Phone, Google Glass, Microsoft Zune, and Netscape Navigator represent fascinating case studies of hardware and platforms that, despite their technical innovations, faced significant challenges in achieving lasting market success. Each offers valuable lessons about the complex relationship between technological capability and commercial viability.</p>
</div>
<div class="sect1">
<h2 id="_windows_phone_microsofts_7_6_billion_lesson">Windows Phone: Microsoft&#8217;s $7.6 Billion Lesson</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Microsoft&#8217;s Windows Phone stands as one of tech&#8217;s most expensive failures, with the company writing off $7.6 billion related to the Nokia acquisition. Despite having superior hardware and a polished user interface, Windows Phone never gained meaningful market share.</p>
</div>
<div class="paragraph">
<p>The platform featured a unique tile-based interface that demonstrated Microsoft&#8217;s innovative approach to mobile design. This Live Tile system showcased Windows Phone&#8217;s dynamic interface capabilities and integration with the broader Windows ecosystem. The platform&#8217;s Metro design language influenced modern UI design principles and demonstrated Microsoft&#8217;s vision for unified experiences across devices.</p>
</div>
<div class="paragraph">
<p>However, Windows Phone&#8217;s failure stemmed from entering the market too late and creating a vicious ecosystem cycle. Microsoft launched Windows Phone 7 in 2010, three years after the iPhone had already transformed the industry. By then, iOS and Android had established dominant positions with hundreds of thousands of apps, while Windows Phone launched with only 2,000. The platform suffered from a destructive cycle where low user adoption meant developers ignored the platform, which in turn meant fewer apps, leading to even lower adoption.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_google_glass_the_wearable_that_wasnt_ready_for_society">Google Glass: The Wearable That Wasn&#8217;t Ready for Society</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Google Glass generated enormous hype as the future of wearable computing but crashed spectacularly due to privacy concerns and social acceptance issues. Despite Google&#8217;s technological prowess, the product was discontinued just two years after its 2013 launch.</p>
</div>
<div class="paragraph">
<p>The Glass platform introduced revolutionary concepts in augmented reality and hands-free computing. Its voice recognition capabilities and heads-up display technology influenced modern AR development and demonstrated the potential for seamless human-computer interaction through voice commands and gesture controls.</p>
</div>
<div class="paragraph">
<p>However, Google Glass faced a perfect storm of problems that made it unsuitable for mainstream adoption. The $1,500 price tag made it inaccessible to most consumers, while the device&#8217;s bulky design and visible camera created immediate privacy concerns. People worried about being recorded without consent, leading to bans in restaurants, bars, and other public spaces. Most critically, Google failed to clearly define the target market and value proposition for everyday users.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_microsoft_zune_the_ipod_killer_that_never_was">Microsoft Zune: The iPod Killer That Never Was</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Microsoft&#8217;s Zune music player launched in 2006 as a direct competitor to Apple&#8217;s iPod but failed to make a significant dent in Apple&#8217;s market dominance. Despite some innovative features, the Zune became synonymous with Microsoft&#8217;s inability to compete in consumer electronics.</p>
</div>
<div class="paragraph">
<p>Zune introduced innovative wireless sharing capabilities and social music discovery features that predated modern streaming services. Its larger screen and improved navigation demonstrated Microsoft&#8217;s understanding of user interface design, while the Zune software provided a more integrated media management experience than many competitors.</p>
</div>
<div class="paragraph">
<p>However, the Zune suffered from classic late-mover disadvantages. By 2006, the iPod had already established itself as the dominant music player, with a mature ecosystem including iTunes and strong brand loyalty. Microsoft&#8217;s device offered improvements like wireless sharing and a larger screen, but these incremental benefits weren&#8217;t enough to overcome Apple&#8217;s head start. Microsoft also struggled with marketing and brand positioning, lacking the sleek design aesthetic that made Apple products desirable.</p>
</div>
</div>
</div>
<h1 id="_conclusion_lessons_in_technology_adoption" class="sect0">Conclusion: Lessons in Technology Adoption</h1>
<div class="paragraph">
<p>The histories of these varied technologies illustrate a critical lesson: technical merit alone is insufficient to guarantee market success. Whether it&#8217;s a programming language like ALGOL that lacked commercial focus, a framework like Japronto whose benchmark performance was impractical for real-world use, or hardware like the Zune and Windows Phone that couldn&#8217;t overcome established ecosystems, the pattern is consistent.</p>
</div>
<div class="paragraph">
<p>Sustainable adoption requires a delicate balance of innovation with practical usability, strong industry support, and the ability to evolve alongside changing user needs and market dynamics. The failure to address real-world problems, build a supportive ecosystem, or adapt to new industry standards ultimately led to the decline of these once-promising technologies.</p>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>A brief introduction to HyperLogLog++</title>
		<link>https://blog.lunatech.com//posts/2025-06-13-hyperloglog</link>
		
		<dc:creator><![CDATA[Alberto]]></dc:creator>
        <pubDate>2025-06-13T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[hyperloglog]]></category>
                }
             {
            <category><![CDATA[algorithm]]></category>
                }
             {
            <category><![CDATA[randomized-algorithm]]></category>
                }
             {
            <category><![CDATA[beyond-the-code]]></category>
                }
             {
            <category><![CDATA[bash-to-the-feature]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-06-13-hyperloglog</guid>

					<description>
                        <![CDATA[ This document demonstrates a fascinating technique that tackles a complex computational problem using an elegant probabilistic strategy.]]></description>
                    <content:encoded><![CDATA[
                    <div id="toc" class="toc">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#introduction">The Problem</a></li>
<li><a href="#_the_algorithm">The Algorithm</a></li>
<li><a href="#_example">Example</a></li>
<li><a href="#_implementation">Implementation</a>
<ul class="sectlevel2">
<li><a href="#_data_structure">Data Structure</a></li>
<li><a href="#_add_operation">Add Operation</a></li>
<li><a href="#_count_operation">Count Operation</a></li>
<li><a href="#_merge_operation">Merge Operation</a></li>
</ul>
</li>
<li><a href="#_relative_percentage_error">Relative Percentage Error</a></li>
<li><a href="#_conclusion">Conclusion</a></li>
</ul>
</div>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This document demonstrates a fascinating technique that tackles a complex computational problem using an elegant probabilistic strategy.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="introduction">The Problem</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <strong>count-distinct problem</strong> (also known as the <em>cardinality estimation problem</em>) involves finding the number of distinct elements in a data stream containing repeated elements.</p>
</div>
<div class="paragraph">
<p>Traditionally, solving this problem requires Θ(D) space complexity, where D represents the number of unique elements. This is because we need to store each unique element to determine whether a new element has been previously encountered.</p>
</div>
<div class="paragraph">
<p>Unfortunately, this approach doesn&#8217;t scale for massive cardinalities found in real-world applications such as:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Unique Query Counting</strong>: Tracking the number of unique searches in a search engine</p>
</li>
<li>
<p><strong>Network Monitoring</strong>: Counting unique source IP addresses</p>
</li>
<li>
<p><strong>Unique Visitor Tracking</strong>: Monitoring distinct users across web platforms</p>
</li>
</ul>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
In the original paper and related literature, "cardinality" refers to the number of distinct elements.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_algorithm">The Algorithm</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When you only need an estimation, or when the number of unique elements makes it impractical to store them all in memory, <strong>HyperLogLog++</strong> provides an excellent solution.</p>
</div>
<div class="paragraph">
<p>This technique estimates the number of unique elements with a relative percentage error between -4% and 6%, using only 16 KB of memory (though this depends on the number of registers configured).</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_example">Example</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let me illustrate the core concept with a simple analogy.</p>
</div>
<div class="paragraph">
<p>Imagine someone spends an entire day rolling a die and tells you the maximum number of consecutive 1s they rolled was 3. While you can&#8217;t determine the exact number of rolls, you can estimate it by calculating the probability of this event occurring.</p>
</div>
<div class="paragraph">
<p>The probability of rolling three consecutive 1s is:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>1/6 * 1/6 * 1/6 = 1/(6^3)</pre>
</div>
</div>
<div class="paragraph">
<p>Therefore, the expected number of trials needed to observe this event is:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>1 / (1/6^3) = 6^3 = 216</pre>
</div>
</div>
<div class="paragraph">
<p>Translating this concept into an algorithm:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The <strong>Dice game</strong> becomes a Hash function that hashes the element in input and yields bits. These bits represent the outcome of the game run, like if the dice had only two faces, 1 and 0.</p>
</li>
<li>
<p>The event taken into account was that the longest sequence of 1&#8217;s (that the die returns 1) at the start of the game, but in this case, since hashing returns a binary output, the algorithm will calculate the event of longest run of leading bits set to 0. Technically, it&#8217;s also possible to count leading ones, but counting leading zeros is a common convention.</p>
</li>
<li>
<p><strong>Probability of rolling a 1 (1/6)</strong> → Probability of a bit being 0 (1/2)</p>
</li>
<li>
<p><strong>Number of plays</strong> → Cardinality</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This represents the fundamental idea behind the Flajolet–Martin algorithm, which HyperLogLog improves upon through three key enhancements.
*Correction Factor* is applied to mitigate the overestimation bias inherent in the standard HLL algorithm.
*Grouped Averaging* specifically the harmonic mean across multiple registers (or buckets), to significantly improve the accuracy of its cardinality estimates.
HLL for small cardinalities suffers from the Overestimation Bias, for that reason <strong>Linear Counting</strong> is used, which is another probabilistic counting algorithm simple and highly accurate for that scale.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_implementation">Implementation</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_data_structure">Data Structure</h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">class HyperLogLog {
    //Number of registers. Must be a power of 2. `buckets = 2^p`
    private final int buckets = 16384;

    // Correction factor
    private final double alpha = 0.7213 / (1 + 1.079 / buckets); //0.72125250052

    //Initialize the array of 16 383 (2^14-1) elements
    private final byte[] registers = new byte[buckets];</code></pre>
</div>
</div>
<div class="paragraph">
<p>The hash function outputs 64 bits, the first 14 are used to address the registers while the last 50 are used for calculating the ranking.
That&#8217;s why the size of the register array is <code>16384</code> and the type is <code>byte</code> sufficient to count up to 50 leading zeros.</p>
</div>
</div>
<div class="sect2">
<h3 id="_add_operation">Add Operation</h3>
<div class="paragraph">
<p>The <code>add</code> operation is how you feed individual elements into the HyperLogLog structure. The goal is to update the internal state of the HLL to reflect the presence of this new element.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">    public void add(String element) {
        // Calculate hash
        long hash = hashElement(element);
        // Take the first 14 bits to address the register
        short registerIndex = getIndex(hash);
        // Take the last 50 bits
        long value = getValue(hash);
        // Calculate the rank value for the target register
        byte rank = (byte) (leadingZeros(value) + 1);
        // Keep the biggest rank between rank and registers[registerIndex]
        if (rank &gt; registers[registerIndex]) {
            registers[registerIndex] = rank;
        }
    }</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Calculate</strong> a 64-bit hash of the input</p>
</li>
<li>
<p><strong>Extract</strong> the first 14 bits of the hash to index the register</p>
</li>
<li>
<p><strong>Use</strong> the remaining 50 bits to calculate the number of leading zeros</p>
</li>
<li>
<p><strong>Compare</strong> with the existing register value and store the biggest rank</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_count_operation">Count Operation</h3>
<div class="paragraph">
<p>The <code>count</code> operation provides an approximation of the number of distinct elements that have been added to the HyperLogLog.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">    public long count() {
        double sum = 0;
        int zeroRegisters = 0;
        // Calculate the harmonic mean of the register values
        for (byte register: registers) {
            sum += Math.pow(2, -register);
            if (register == 0) {
                zeroRegisters++;
            }
        }
        // Raw estimate
        double estimate = alpha * buckets * buckets / sum;
        // Apply corrections for small cardinalities
        if (estimate &lt;= 2.5 * m &amp;&amp; zeroRegisters &gt; 0) {
            // Linear counting
            estimate = buckets * Math.log((double) buckets / zeroRegisters);
        }
        return Math.round(estimate);
    }</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Calculate</strong> the harmonic mean of all register values</p>
</li>
<li>
<p><strong>Apply</strong> a correction factor</p>
</li>
<li>
<p><strong>Count</strong> the number of empty registers</p>
</li>
<li>
<p><strong>Fall back</strong> to linear counting for small cardinalities</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_merge_operation">Merge Operation</h3>
<div class="paragraph">
<p>The <code>merge</code> operation allows you to combine two or more HyperLogLog structures into a new HLL structure (or update one with another). This is a powerful feature for distributed systems where distinct counts might be computed on subsets of data in parallel and then combined.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">    public void merge(HyperLogLog that) {
        for (int i = 0; i &lt; buckets; i++) {
            if (this.registers[i] &lt; that.registers[i])
                this.registers[i] = that.registers[i];
        }
    }</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Compare</strong> each register pair at the same index</p>
</li>
<li>
<p><strong>Retain</strong> the register with the larger value</p>
</li>
</ol>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_relative_percentage_error">Relative Percentage Error</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="../media/2025-06-13-hyperloglog/error_plot.svg" alt="Error Plot">
</div>
</div>
<div class="paragraph">
<p>In this graph x-axis represents the expected cardinality, and it goes from 0 to 100k elements.
While the y-axis represents the relative error in percentage.
We can observe that in the first part that linear counting is used for cardinalities up to approximately 40,000, after which HyperLogLog++ takes over, the error is quite high, but it starts to converge around 0 the more elements come in.
The next graph shows what would happen without linear counting, the Overestimation Bias.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-06-13-hyperloglog/no_linear_counting.svg" alt="No Linear Counting">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>HyperLogLog++ is a good solution for counting unique elements in those use case where storing them in memory is not practical.
It provides O(1) complexity both in terms of time and space, and the relative error is in the range of -4% to 6%.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-06-13-hyperloglog/flajolet_philippe_small.jpg" alt="Philippe Flajolet">
</div>
</div>
<div class="paragraph">
<p><em>Philippe Flajolet - First author of "HyperLogLog: the analysis of a near-optimal
cardinality estimation algorithm"</em></p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>A Quest to Tame Large Language Models</title>
		<link>https://blog.lunatech.com//posts/2025-05-26-demystify-LLMs</link>
		
		<dc:creator><![CDATA[Luke Woodcock]]></dc:creator>
        <pubDate>2025-05-26T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[LLM]]></category>
                }
             {
            <category><![CDATA[natural language processing]]></category>
                }
             {
            <category><![CDATA[transformers]]></category>
                }
             {
            <category><![CDATA[machine learning]]></category>
                }
             {
            <category><![CDATA[AI]]></category>
                }
             {
            <category><![CDATA[language models]]></category>
                }
             {
            <category><![CDATA[probabilistic text generation]]></category>
                }
             {
            <category><![CDATA[statistical language models]]></category>
                }
             {
            <category><![CDATA[n-gram]]></category>
                }
             {
            <category><![CDATA[bigram]]></category>
                }
             {
            <category><![CDATA[attention mechanisms]]></category>
                }
             {
            <category><![CDATA[vector embeddings]]></category>
                }
             {
            <category><![CDATA[tokenization]]></category>
                }
             {
            <category><![CDATA[context windows]]></category>
                }
             {
            <category><![CDATA[self-attention]]></category>
                }
             {
            <category><![CDATA[neural networks]]></category>
                }
             {
            <category><![CDATA[NLP fundamentals]]></category>
                }
             {
            <category><![CDATA[AI architecture]]></category>
                }
             {
            <category><![CDATA[language understanding]]></category>
                }
             {
            <category><![CDATA[transformer architecture]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-05-26-demystify-LLMs</guid>

					<description>
                        <![CDATA[ Large Language Models represent one of the most significant advances in artificial intelligence, fundamentally transforming how we interact with natural language processing systems. Understanding their architecture and mechanisms is essential for anyone working with modern AI systems.]]></description>
                    <content:encoded><![CDATA[
                    <div id="toc" class="toc">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#introduction">Introduction</a></li>
<li><a href="#_1_the_foundation_probabilistic_text_generation">1. The Foundation: Probabilistic Text Generation</a>
<ul class="sectlevel2">
<li><a href="#_statistical_language_models">Statistical Language Models</a></li>
<li><a href="#_bi_gram_models">Bi-gram Models</a></li>
<li><a href="#_n_gram_models">N-gram Models</a></li>
</ul>
</li>
<li><a href="#_2_the_transformer_revolution_attention_is_all_you_need">2. The Transformer Revolution: "Attention is All You Need"</a>
<ul class="sectlevel2">
<li><a href="#_vector_representations">Vector Representations</a></li>
<li><a href="#_attention_mechanisms">Attention Mechanisms</a></li>
<li><a href="#_attention_architecture">Attention Architecture</a></li>
</ul>
</li>
<li><a href="#_technical_glossary">Technical Glossary</a>
<ul class="sectlevel2">
<li><a href="#_attention_mechanism">attention mechanism</a></li>
<li><a href="#_bigram_model">bigram model</a></li>
<li><a href="#_byte_pair_encoding">byte-pair encoding</a></li>
<li><a href="#_context">context</a></li>
<li><a href="#_corpus">corpus</a></li>
<li><a href="#_data_sparsity">data sparsity</a></li>
<li><a href="#_embedding">embedding</a></li>
<li><a href="#_epoch">epoch</a></li>
<li><a href="#_hyperparameter">hyperparameter</a></li>
<li><a href="#_long_range_dependencies">long range dependencies</a></li>
<li><a href="#_loss">loss</a></li>
<li><a href="#_n_gram_model">n-gram model</a></li>
<li><a href="#_over_fitting">over-fitting</a></li>
<li><a href="#_parameter">parameter</a></li>
<li><a href="#_parameter_space">parameter space</a></li>
<li><a href="#_preprocessing">preprocessing</a></li>
<li><a href="#_temperature">temperature</a></li>
<li><a href="#_token">token</a></li>
<li><a href="#_tokenization">tokenization</a></li>
<li><a href="#_training">training</a></li>
<li><a href="#_transformer">transformer</a></li>
<li><a href="#_vector">vector</a></li>
</ul>
</li>
</ul>
</div>
<div class="sect1">
<h2 id="introduction">Introduction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Large Language Models represent one of the most significant advances in artificial intelligence, fundamentally transforming how we interact with natural language processing systems. Understanding their architecture and mechanisms is essential for anyone working with modern AI systems.</p>
</div>
<div class="paragraph">
<p>This guide examines the core concepts underlying LLMs, from foundational statistical models to sophisticated attention mechanisms, providing practical insights for implementation and deployment. We&#8217;ll explore how these systems generate coherent text, the role of context in language understanding, and the architectural innovations that enable modern capabilities.</p>
</div>
<div class="paragraph">
<p>If new to the language of LLMs, this guide aims to demystify some of the "magic" surrounding them. If already versed in the language of LLMs, this guide hopes to be a refresher.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_the_foundation_probabilistic_text_generation">1. The Foundation: Probabilistic Text Generation</h2>
<div class="sectionbody">
<div class="imageblock">
<div class="content">
<img src="../media/2025-05-26-demystify-LLMs/cotton-probability-machine-simple-compose.jpg" alt="Embroidered probability machine" width="900">
</div>
</div>
<div class="paragraph">
<p>Large Language Models operate as sophisticated probability machines. At their core, they analyze patterns in text data to predict the most likely next token given a specific context. While they incorporate stochastic elements through temperature controls, pedantically, their underlying mechanisms are fundamentally deterministic—the same prompt with identical parameters will consistently produce the same output. In pratice, however, they are far from determinisitic—the stochastic elements and numerous other caveats render that statement for "illustration purposes only".</p>
</div>
<div class="sect2">
<h3 id="_statistical_language_models">Statistical Language Models</h3>
<div class="paragraph">
<p>Statistical language models form the conceptual foundation for understanding modern LLMs. These models process a <a href="#_corpus">corpus</a> of text and use statistical patterns to predict subsequent words or <a href="#_tokenization">tokens</a>. While contemporary LLMs have evolved far beyond these simple approaches, understanding statistical models illuminates the core challenges that advanced architectures address.</p>
</div>
</div>
<div class="sect2">
<h3 id="_bi_gram_models">Bi-gram Models</h3>
<div class="paragraph">
<p>A <a href="#_bigram_model">bigram model</a> represents the simplest form of statistical language modeling. It analyzes pairs of consecutive words to build frequency tables that inform predictions.</p>
</div>
<div class="paragraph">
<p>Consider this example corpus:
<em>"The cat sat on the mat. The mat was soft and warm."</em></p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-05-26-demystify-LLMs/cat_on_warm_mat_simple_compose.jpg" alt="Embroidered cat on an embroidered mat" width="900">
</div>
</div>
<div class="paragraph">
<p>The resulting bi-gram frequency table would contain:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Bigram</th>
<th class="tableblock halign-left valign-top">Count</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">The cat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">cat sat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">sat on</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">on the</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">the mat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">mat The</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">was soft</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">soft and</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">and warm</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>When processing the input "The," the model examines all bi-grams beginning with "The":</p>
</div>
<div class="ulist">
<ul>
<li>
<p>"The cat" (1 occurrence)</p>
</li>
<li>
<p>"The mat" (2 occurrences)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The model predicts "mat" as the most probable next word based on frequency.</p>
</div>
<div class="paragraph">
<p>While effective for demonstration, <a href="#_bigram_model">bigram models</a> suffer from severe contextual limitations, because the consider only one preceding word for their predictions.</p>
</div>
</div>
<div class="sect2">
<h3 id="_n_gram_models">N-gram Models</h3>
<div class="paragraph">
<p>The <a href="#_n_gram_model">n-gram model</a> extends the bi-gram concept by incorporating longer <a href="#_context">contextual windows</a>. A trigram model, for example, considers two preceding words, while an n-gram model employs n-1 words of context.</p>
</div>
<div class="paragraph">
<p>Let&#8217;s look at the sentence: <em>"Thank you very much for your cooperation. I very much appreciated it. We very much made progress."</em></p>
</div>
<div class="paragraph">
<p>A trigram model encountering "you very" would leverage both "you" and "very" to predict "much," using conditional probability <strong><em>P("much" | "you", "very")</em>.</strong></p>
</div>
<div class="paragraph">
<p>The relationship between context length and model performance involves critical trade-offs:</p>
</div>
<div class="paragraph">
<p><strong>Longer Context (Higher n):</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Captures richer contextual dependencies</p>
</li>
<li>
<p>Enables more coherent text generation</p>
</li>
<li>
<p>Increases model complexity and <a href="#_parameter_space">parameter space</a></p>
</li>
<li>
<p>Higher risk of <a href="#_data_sparsity">data sparsity</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Shorter Context (Lower n):</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Simpler models with fewer parameters</p>
</li>
<li>
<p>More robust probability estimates</p>
</li>
<li>
<p>Limited contextual understanding</p>
</li>
<li>
<p>Reduced coherence in generated text</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><a href="#_data_sparsity">Data sparsity</a> becomes increasingly problematic as n increases—many <a href="#_n_gram_model">n-grams</a> may not appear frequently enough in training data to provide reliable probability estimates.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_the_transformer_revolution_attention_is_all_you_need">2. The Transformer Revolution: "Attention is All You Need"</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The transformer architecture, introduced by Google researchers, revolutionized natural language processing by solving the contextual limitations of <a href="#_n_gram_model">n-gram models</a> through sophisticated <a href="#_attention_mechanism">attention mechanisms</a>.</p>
</div>
<div class="sect2">
<h3 id="_vector_representations">Vector Representations</h3>
<div class="paragraph">
<p><a href="#_transformer">Transformers</a> convert words and <a href="#_token">tokens</a> into high-dimensional <a href="#_vector">vectors</a> (<a href="#_embedding">embeddings</a>) that capture semantic and syntactic relationships. Unlike sequential models that process text word-by-word, transformers can analyze relationships between all words in a passage simultaneously.</p>
</div>
<div class="paragraph">
<p><strong>Vector Dimensionality:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>2D vectors contain 2 numbers (analogous to map coordinates)</p>
</li>
<li>
<p>3D vectors contain 3 numbers (spatial coordinates)</p>
</li>
<li>
<p>LLM vectors are high-dimensional with hundreds or thousands of dimensions</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Each dimension in an <a href="#_vector">vector</a> space captures different aspects of meaning, enabling the model to represent complex relationships between words and concepts. Vectors occupying similar positions in this space represent semantically related concepts.</p>
</div>
</div>
<div class="sect2">
<h3 id="_attention_mechanisms">Attention Mechanisms</h3>
<div class="paragraph">
<p>An <a href="#_attention_mechanism">attention mechanism</a> functions as a dynamic spotlight, highlighting relevant information during text processing. For each <a href="#_token">token</a>, the model calculates attention weights determining how much focus to allocate to every other token in the context.</p>
</div>
<div class="paragraph">
<p><strong>Key Advantages:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Long-range Dependencies:</strong> Links related information across distant text portions</p>
</li>
<li>
<p><strong>Context-Aware Processing:</strong> Resolves ambiguous words based on surrounding context</p>
</li>
<li>
<p><strong>Parallel Processing:</strong> Analyzes all relationships simultaneously rather than sequentially</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_attention_architecture">Attention Architecture</h3>
<div class="paragraph">
<p><a href="#_attention_mechanism">Attention mechanisms</a> operate through three primary components for each <a href="#_token">token</a>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Query Vector:</strong> Represents what the current token is "looking for"</p>
</li>
<li>
<p><strong>Key Vector:</strong> Represents what each token "offers" as context</p>
</li>
<li>
<p><strong>Value Vector:</strong> Contains the actual information to be combined</p>
</li>
</ol>
</div>
<div class="paragraph">
<p><strong>Processing Steps:</strong></p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Generate query, key, and value vectors for each token</p>
</li>
<li>
<p>Compare the current token&#8217;s query with all tokens' keys</p>
</li>
<li>
<p>Calculate attention scores indicating relevance strength</p>
</li>
<li>
<p>Use scores to weight value vectors</p>
</li>
<li>
<p>Combine weighted values to produce final token representation</p>
</li>
</ol>
</div>
<div class="paragraph">
<p><strong>Attention Weight Properties:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Higher weights indicate stronger relevance</p>
</li>
<li>
<p>Weights are normalized to form probability distributions (sum to 1)</p>
</li>
<li>
<p>Enable the model to focus on the most contextually relevant information</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Self-Attention:</strong></p>
</div>
<div class="paragraph">
<p>Every token in a sequence attends to all others, including itself, capturing comprehensive contextual relationships across the entire sequence.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_technical_glossary">Technical Glossary</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_attention_mechanism">attention mechanism</h3>
<div class="paragraph">
<p>Mechanisms that enable models to weigh the importance of different input portions relative to each other, focusing on the most relevant information for accurate and coherent output generation.</p>
</div>
</div>
<div class="sect2">
<h3 id="_bigram_model">bigram model</h3>
<div class="paragraph">
<p>Statistical models that predict the next word based on the immediately preceding word, analyzing word pair frequencies to determine probability distributions.</p>
</div>
</div>
<div class="sect2">
<h3 id="_byte_pair_encoding">byte-pair encoding</h3>
<div class="paragraph">
<p>An algorithm for creating efficient tokenization by:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Counting character frequencies in the corpus</p>
</li>
<li>
<p>Identifying the most common character pairs</p>
</li>
<li>
<p>Adding common pairs to the vocabulary</p>
</li>
<li>
<p>Iteratively building tokens from frequent patterns</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_context">context</h3>
<div class="paragraph">
<p>The surrounding words or sequences that inform next-word prediction. Context length varies by model type—<a href="#_bigram_model">bigram models</a> use 1 word, trigram models use 2 words, and <a href="#_n_gram_model">ngram models</a> use n-1 words of context.</p>
</div>
</div>
<div class="sect2">
<h3 id="_corpus">corpus</h3>
<div class="paragraph">
<p>The comprehensive dataset of texts used for model training, typically including diverse sources such as books, articles, websites, and other written materials. Corpus quality and diversity directly impact model performance.</p>
</div>
</div>
<div class="sect2">
<h3 id="_data_sparsity">data sparsity</h3>
<div class="paragraph">
<p>Insufficient coverage of possible inputs or features in training data, where certain patterns may not appear frequently enough to provide reliable probability estimates.</p>
</div>
</div>
<div class="sect2">
<h3 id="_embedding">embedding</h3>
<div class="paragraph">
<p>Embeddings transform symbolic language into mathematical forms (the vector) that neural networks can process, with similar concepts positioned closer together in the vector space.</p>
</div>
<div class="paragraph">
<p>The terms "embedding" and "vector" are often used interchangeably in machine learning contexts, though "embedding" specifically speaks to the process of transforming data into the vector form, whereas "an embedding" likely speaks to a vector—unless a new model is invented that doesn&#8217;t use vectors.</p>
</div>
</div>
<div class="sect2">
<h3 id="_epoch">epoch</h3>
<div class="paragraph">
<p>One complete pass through the entire training dataset, during which the model processes all examples and updates parameters based on prediction errors.</p>
</div>
</div>
<div class="sect2">
<h3 id="_hyperparameter">hyperparameter</h3>
<div class="paragraph">
<p>A configuration setting that influences model behavior but is not learned during training. Examples include learning rate, batch size, and temperature. Hyperparameters are typically set before training begins and can significantly impact model performance.</p>
</div>
</div>
<div class="sect2">
<h3 id="_long_range_dependencies">long range dependencies</h3>
<div class="paragraph">
<p>Relationships between words or phrases separated by significant distances in text, such as pronouns referring to entities in different paragraphs.</p>
</div>
</div>
<div class="sect2">
<h3 id="_loss">loss</h3>
<div class="paragraph">
<p>A metric measuring prediction accuracy by quantifying the difference between model outputs and correct answers. Training progressively reduces loss through parameter optimization.</p>
</div>
</div>
<div class="sect2">
<h3 id="_n_gram_model">n-gram model</h3>
<div class="paragraph">
<p>Describes the general version of a bigram model. It is a statistical language modeling approach that predicts words based on n-1 previous words in sequence. Common variants include bigrams (n=2), trigrams (n=3).</p>
</div>
</div>
<div class="sect2">
<h3 id="_over_fitting">over-fitting</h3>
<div class="paragraph">
<p>A condition where models perform exceptionally on training data but fail to generalize to new, unseen inputs—analogous to memorizing without understanding.</p>
</div>
</div>
<div class="sect2">
<h3 id="_parameter">parameter</h3>
<div class="paragraph">
<p>The individual weights and biases within a model that are adjusted during training to minimize prediction error. Parameters are learned from the training data and define the model&#8217;s behavior.</p>
</div>
</div>
<div class="sect2">
<h3 id="_parameter_space">parameter space</h3>
<div class="paragraph">
<p>The multidimensional mathematical domain that encompasses all the weights and biases that the model can learn, which can number in the millions or billions for modern language models.</p>
</div>
</div>
<div class="sect2">
<h3 id="_preprocessing">preprocessing</h3>
<div class="paragraph">
<p>Data preparation steps including cleaning, transformation, and structuring to optimize datasets for machine learning, such as lowercasing text or removing stop words.</p>
</div>
</div>
<div class="sect2">
<h3 id="_temperature">temperature</h3>
<div class="paragraph">
<p>A hyperparameter controlling output randomness:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Lower Temperature:</strong> More deterministic, focused responses with higher probability words</p>
</li>
<li>
<p><strong>Higher Temperature:</strong> Increased randomness and creativity, selecting less probable words</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Not to be confused with the parameter space.</p>
</div>
</div>
<div class="sect2">
<h3 id="_token">token</h3>
<div class="paragraph">
<p>The fundamental unit of text processed by language models, representing a piece of text produced through tokenization. Tokens can be words, subwords, characters, or other linguistic units depending on the tokenization method used.</p>
</div>
</div>
<div class="sect2">
<h3 id="_tokenization">tokenization</h3>
<div class="paragraph">
<p>The process of segmenting text into smaller units (tokens) such as words, subwords, or characters. Effective tokenization increases training examples and enables models to learn morphological patterns.</p>
</div>
</div>
<div class="sect2">
<h3 id="_training">training</h3>
<div class="paragraph">
<p>The process of optimizing the model&#8217;s parameter space to maximize prediction accuracy, expressed as f(x|params) where x represents input and params represents learned weights.</p>
</div>
</div>
<div class="sect2">
<h3 id="_transformer">transformer</h3>
<div class="paragraph">
<p>A neural network architecture that consists of encoders (for understanding input) and decoders (for generating output), or decoder-only (for generative tasks). Transformers process all tokens in parallel rather than sequentially and better capture long range context.</p>
</div>
</div>
<div class="sect2">
<h3 id="_vector">vector</h3>
<div class="paragraph">
<p>High-dimensional numerical representations of text or data, capturing semantic and syntactic relationships in mathematical space suitable for computational processing.</p>
</div>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Part 4: Angular 19 Deep Dive – Smarter Forms with Signals and Control Flow</title>
		<link>https://blog.lunatech.com//posts/2025-05-20-part-4:-angular-19-deep-dive-–-smarter-forms-with-signals-and-control-flow</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-05-20T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[angular]]></category>
                }
             {
            <category><![CDATA[nestjs]]></category>
                }
             {
            <category><![CDATA[postgresql]]></category>
                }
             {
            <category><![CDATA[typeorm]]></category>
                }
             {
            <category><![CDATA[jwt]]></category>
                }
             {
            <category><![CDATA[authentication]]></category>
                }
             {
            <category><![CDATA[frontend]]></category>
                }
             {
            <category><![CDATA[typescript]]></category>
                }
             {
            <category><![CDATA[nodejs]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-05-20-part-4:-angular-19-deep-dive-–-smarter-forms-with-signals-and-control-flow</guid>

					<description>
                        <![CDATA[ Welcome back to the Full-Stack Authentication Boilerplate (Angular +]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Welcome back to the Full-Stack Authentication Boilerplate (Angular<br>
NestJS + PostgreSQL) series. So far, we’ve wired up the backend and
built a working Angular frontend. Now it’s time to modernize our forms
using the latest Angular 19 features—specifically <strong>Signals</strong>, <strong>control
flow syntax</strong>, and <strong>defer blocks</strong>.</p>
</div>
<div class="paragraph">
<p>Angular 19 isn’t a total rewrite—it’s a refinement. It tightens up
template logic, improves reactivity, and lets us write cleaner, more
performant UI code. In this part, we’ll refactor the login and register
forms to take full advantage of these improvements.</p>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_angular_19_recap_and_what_actually_matters_for_devs">Angular 19 Recap (and What Actually Matters for Devs)</h3>
<div class="paragraph">
<p>Here’s what we’ll use in this part:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>✅ <strong>Signals</strong>: Angular’s native reactive primitive, lets you track
state like <code>useState()</code> in React—but fully integrated into Angular’s
change detection.</p>
</li>
<li>
<p>✅ <strong>Control Flow Syntax</strong> (<code>@if</code>, <code>@for</code>, <code>@switch</code>): Cleaner
alternatives to structural directives like <code>*ngIf</code> and <code>*ngFor</code>.</p>
</li>
<li>
<p>✅ <strong>Defer Blocks</strong>: Lazy-load parts of the UI, just like backend routes
or feature modules.</p>
</li>
<li>
<p>✅ <strong>@let Directive</strong>: Declare local variables directly in templates.</p>
</li>
<li>
<p>✅ <strong>Smarter Change Detection</strong>: Updates only the DOM parts that
actually changed, for faster UIs.</p>
</li>
</ul>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>For backend devs:</strong> Think of <code>signal()</code> like a reactive field or an
in-memory tracked variable; <code>@if</code>/<code>@for</code> in your templates is like
having expressive, inline logic with instant UI updates—no more verbose
boilerplate.</p>
</div>
</blockquote>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_signals_or_reactive_forms">Signals or Reactive Forms?</h3>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>When to Use Each (for Backend Devs):</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Signals</strong> are perfect for local/component UI state and simple forms.</p>
</li>
<li>
<p><strong>Reactive Forms</strong> (<code>FormBuilder</code>, etc.) are the gold standard for
complex, validation-heavy, or dynamic forms—especially if you need
granular error handling or will scale up forms later.</p>
</li>
<li>
<p><strong>Pro Tip:</strong> You can use both! Track form state with Reactive Forms,
then reflect with signals using Angular’s <code>toSignal()</code> utility for the
best DX.</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">// Example: Bridge form values to signals
formValue = toSignal(form.valueChanges, { initialValue: form.value });</code></pre>
</div>
</div>
</blockquote>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_migration_ngmodules_standalone_components">Migration: NgModules → Standalone Components</h3>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>Old way:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">@NgModule({
  declarations: [LoginComponent],
  imports: [ReactiveFormsModule],
})
export class AuthModule {}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>New way (Angular 15+):</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">@Component({
  standalone: true,
  selector: 'app-login',
  templateUrl: './login.component.html',
  imports: [ReactiveFormsModule],
})
export class LoginComponent {}</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>No more <code>@NgModule</code> needed.</p>
</li>
<li>
<p>Use <code>standalone: true</code> and import dependencies directly in the
component.</p>
</li>
<li>
<p>Both patterns can coexist during migration.</p>
</li>
</ul>
</div>
</blockquote>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_refactoring_the_login_form">Refactoring the Login Form</h3>
<div class="paragraph">
<p>Here’s how to use Reactive Forms as the foundation, enhanced with
signals and modern control flow blocks.</p>
</div>
<div class="sect3">
<h4 id="_login_component_ts"><code>login.component.ts</code></h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import {
  Component,
  computed,
  signal,
  inject,
  ChangeDetectionStrategy,
} from '@angular/core';
import { Router } from '@angular/router';
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from '../../services/auth.service';
import { toSignal } from '@angular/core/rxjs-interop';
import { take } from 'rxjs/operators';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
  standalone: true,
  imports: [ReactiveFormsModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoginComponent {
  private readonly fb = inject(FormBuilder);
  readonly form = this.fb.group({
    email: ['', [Validators.required, Validators.email]],
    password: ['', [Validators.required, Validators.minLength(6)]],
  });

  readonly isLoading = signal(false);
  readonly errorMessage = signal&lt;string | null&gt;(null);

  readonly formValue = toSignal(this.form.valueChanges, {
    initialValue: this.form.value,
  });
  readonly formStatus = toSignal(this.form.statusChanges, {
    initialValue: this.form.status,
  });
  readonly isFormValid = computed(() =&gt; this.formStatus() === 'VALID');

  constructor(
    private readonly auth: AuthService,
    private readonly router: Router
  ) {}

  onSubmit() {
    if (!this.isFormValid()) return;

    const { email, password } = this.form.getRawValue();
    if (!email || !password) return;

    this.isLoading.set(true);
    this.errorMessage.set(null);

    this.auth
      .login({ email, password })
      .pipe(take(1))
      .subscribe({
        next: () =&gt; {
          this.isLoading.set(false);
          this.router.navigate(['/welcome']);
        },
        error: (err) =&gt; {
          this.isLoading.set(false);
          const message =
            err?.error?.message ||
            err?.message ||
            'Login failed. Please try again.';
          this.errorMessage.set(message);
        },
      });
  }
}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="_migration_control_flow_syntax">Migration: Control Flow Syntax</h4>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>Before (classic Angular):</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;div *ngIf="loading"&gt;Loading...&lt;/div&gt;
&lt;ul&gt;
  &lt;li *ngFor="let user of users"&gt;{{ user.name }}&lt;/li&gt;
&lt;/ul&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>After (Angular 17+):</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@if (loading) {
  &lt;div&gt;Loading...&lt;/div&gt;
}
&lt;ul&gt;
  @for (user of users) {
    &lt;li&gt;{{ user.name }}&lt;/li&gt;
  }
&lt;/ul&gt;</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>The new syntax is cleaner and more readable.</p>
</li>
<li>
<p>You can use both during your migration to Angular 17+.</p>
</li>
</ul>
</div>
</blockquote>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="_login_component_html"><code>login.component.html</code></h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;form
  [formGroup]="form"
  (ngSubmit)="onSubmit()"
  class="mt-4 p-4 border rounded shadow-sm bg-white"
  style="max-width: 400px; margin: auto"
&gt;
  &lt;h2 class="text-center mb-4"&gt;Login&lt;/h2&gt;

  &lt;div class="mb-3"&gt;
    @let isInvalidEmail = form.get('email')?.invalid &amp;&amp; form.get('email')?.touched;
    &lt;input
      formControlName="email"
      type="email"
      class="form-control"
      placeholder="Email"
      [class.is-invalid]="isInvalidEmail"
      aria-label="Email"
    /&gt;
    @if (isInvalidEmail) {
      &lt;div class="invalid-feedback"&gt;Please enter a valid email.&lt;/div&gt;
    }
  &lt;/div&gt;

  &lt;div class="mb-3"&gt;
    @let isInvalidPassword = form.get('password')?.invalid &amp;&amp; form.get('password')?.touched;
    &lt;input
      formControlName="password"
      type="password"
      class="form-control"
      placeholder="Password"
      [class.is-invalid]="isInvalidPassword"
      aria-label="Password"
    /&gt;
    @if (isInvalidPassword) {
      &lt;div class="invalid-feedback"&gt;
        Password must be at least 6 characters long.
      &lt;/div&gt;
    }
  &lt;/div&gt;

  @if (errorMessage()) {
    &lt;div class="alert alert-danger"&gt;{{ errorMessage() }}&lt;/div&gt;
  }

  &lt;button
    type="submit"
    class="btn btn-primary w-100"
    [disabled]="isLoading() || !isFormValid()"
  &gt;
    @if (isLoading()) {
      &lt;span class="spinner-border spinner-border-sm me-2"&gt;&lt;/span&gt;
    }
    Login
  &lt;/button&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_refactoring_the_register_form">Refactoring the Register Form</h3>
<div class="sect3">
<h4 id="_register_component_ts"><code>register.component.ts</code></h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import {
  Component,
  computed,
  signal,
  inject,
  ChangeDetectionStrategy,
} from '@angular/core';
import { Router } from '@angular/router';
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from '../../services/auth.service';
import { toSignal } from '@angular/core/rxjs-interop';
import { take } from 'rxjs/operators';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.scss'],
  standalone: true,
  imports: [ReactiveFormsModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RegisterComponent {
  private readonly fb = inject(FormBuilder);
  readonly form = this.fb.group({
    name: ['', [Validators.required]],
    email: ['', [Validators.required, Validators.email]],
    password: ['', [Validators.required, Validators.minLength(6)]],
    role: ['user', [Validators.required]],
  });

  readonly isLoading = signal(false);
  readonly errorMessage = signal&lt;string | null&gt;(null);

  readonly formValue = toSignal(this.form.valueChanges, {
    initialValue: this.form.value,
  });
  readonly formStatus = toSignal(this.form.statusChanges, {
    initialValue: this.form.status,
  });
  readonly isFormValid = computed(() =&gt; this.formStatus() === 'VALID');

  constructor(
    private readonly auth: AuthService,
    private readonly router: Router
  ) {}

  onSubmit() {
    if (!this.isFormValid()) return;

    const { name, email, password, role } = this.form.getRawValue();
    if (!name || !email || !password || !role) return;

    this.isLoading.set(true);
    this.errorMessage.set(null);

    this.auth
      .register({ name, email, password, role })
      .pipe(take(1))
      .subscribe({
        next: () =&gt; {
          this.isLoading.set(false);
          this.router.navigate(['/login']);
        },
        error: (err) =&gt; {
          this.isLoading.set(false);
          const message =
            err?.error?.message ||
            err?.message ||
            'Registration failed. Please try again.';
          this.errorMessage.set(message);
        },
      });
  }
}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="_register_component_html"><code>register.component.html</code></h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;form
  [formGroup]="form"
  (ngSubmit)="onSubmit()"
  class="mt-4 p-4 border rounded shadow-sm bg-white"
  style="max-width: 400px; margin: auto"
&gt;
  &lt;h2 class="text-center mb-4"&gt;Register&lt;/h2&gt;

  &lt;div class="mb-3"&gt;
    @let isInvalidName = form.get('name')?.invalid &amp;&amp; form.get('name')?.touched;
    &lt;input
      formControlName="name"
      type="text"
      class="form-control"
      placeholder="Name"
      [class.is-invalid]="isInvalidName"
      aria-label="Name"
    /&gt;
    @if (isInvalidName) {
      &lt;div class="invalid-feedback"&gt;Name is required.&lt;/div&gt;
    }
  &lt;/div&gt;

  &lt;div class="mb-3"&gt;
    @let isInvalidEmail = form.get('email')?.invalid &amp;&amp; form.get('email')?.touched;
    &lt;input
      formControlName="email"
      type="email"
      class="form-control"
      placeholder="Email"
      [class.is-invalid]="isInvalidEmail"
      aria-label="Email"
    /&gt;
    @if (isInvalidEmail) {
      &lt;div class="invalid-feedback"&gt;Please enter a valid email.&lt;/div&gt;
    }
  &lt;/div&gt;

  &lt;div class="mb-3"&gt;
    @let isInvalidPassword = form.get('password')?.invalid &amp;&amp; form.get('password')?.touched;
    &lt;input
      formControlName="password"
      type="password"
      class="form-control"
      placeholder="Password"
      [class.is-invalid]="isInvalidPassword"
      aria-label="Password"
    /&gt;
    @if (isInvalidPassword) {
      &lt;div class="invalid-feedback"&gt;
        Password must be at least 6 characters long.
      &lt;/div&gt;
    }
  &lt;/div&gt;

  @if (errorMessage()) {
    &lt;div class="alert alert-danger"&gt;{{ errorMessage() }}&lt;/div&gt;
  }

  &lt;button
    type="submit"
    class="btn btn-success w-100"
    [disabled]="isLoading() || !isFormValid()"
  &gt;
    @if (isLoading()) {
      &lt;span class="spinner-border spinner-border-sm me-2"&gt;&lt;/span&gt;
    }
    Register
  &lt;/button&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_real_world_patterns_for_backend_devs">Real-World Patterns for Backend Devs</h3>
<div class="sect3">
<h4 id="_signals_vs_observables">✅ Signals vs Observables</h4>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>Rule of Thumb:</strong> Use signals for local, component-level UI state. Use
observables for async or backend-driven data.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p><strong>Signals (local state):</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">isModalOpen = signal(false);

openModal() { this.isModalOpen.set(true); }
closeModal() { this.isModalOpen.set(false); }</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@if (isModalOpen()) {
  &lt;app-modal (close)="closeModal()"&gt;&lt;/app-modal&gt;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Observables (async state):</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">private modalSubject = new BehaviorSubject(false);
isModalOpen$ = this.modalSubject.asObservable();

openModal() { this.modalSubject.next(true); }
closeModal() { this.modalSubject.next(false); }</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@let isOpen = (isModalOpen$ | async);
@if (isOpen) {
  &lt;app-modal (close)="closeModal()"&gt;&lt;/app-modal&gt;
}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="_control_flow_in_action">✅ Control Flow in Action</h4>
<div class="paragraph">
<p><strong>Using <code>@for</code> instead of <code>*ngFor</code>:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;ul&gt;
  @for (user of users; track user.id) {
    &lt;li&gt;{{ user.name }}&lt;/li&gt;
  }
&lt;/ul&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Using <code>@switch</code> vs <code>*ngSwitch</code>:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@switch (status) {
  @case ('loading') {
    &lt;p&gt;Loading...&lt;/p&gt;
  } @case ('error') {
    &lt;p&gt;Error!&lt;/p&gt;
  } @default {
    &lt;p&gt;All good.&lt;/p&gt;
  }
}</code></pre>
</div>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>Backend parallel:</strong> <code>@if</code>, <code>@for</code>, and <code>@switch</code> are like inline logic
in your backend template engines (e.g., EJS, Razor, Thymeleaf)—but here
they’re fully reactive and type-safe.</p>
</div>
</blockquote>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="_lazy_loading_and_ux_patterns_with_defer">✅ Lazy Loading and UX Patterns with @defer</h4>
<div class="paragraph">
<p>Angular’s <code>@defer</code> block is great for loading UI only when needed.</p>
</div>
<div class="paragraph">
<p><strong>Basic Usage:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@defer (when isHeavyComponentVisible) {
  &lt;app-heavy-widget&gt;&lt;/app-heavy-widget&gt;
} @placeholder {
  &lt;p&gt;Loading widget...&lt;/p&gt;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Loading feedback:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@defer (when dataReady; loading minimum 300ms) {
  &lt;app-dashboard&gt;&lt;/app-dashboard&gt;
} @loading {
  &lt;p&gt;Loading dashboard...&lt;/p&gt;
} @placeholder {
  &lt;p&gt;Initializing view...&lt;/p&gt;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Viewport entry:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@defer (on viewport) {
  &lt;app-news-feed&gt;&lt;/app-news-feed&gt;
} @placeholder {
  &lt;p&gt;Loading news feed when visible...&lt;/p&gt;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Idle trigger:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">@defer (on idle) {
  &lt;app-recommendations&gt;&lt;/app-recommendations&gt;
}</code></pre>
</div>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>⏳ <strong>Why care?</strong> Optimizes TTI (time to interactive) on heavy or mobile
pages.</p>
</div>
</blockquote>
</div>
<hr>
</div>
<div class="sect3">
<h4 id="_smart_change_detection_in_a_dashboard">✅ Smart Change Detection in a Dashboard</h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">userCount = signal(0);
orderTotal = signal(0);

ngOnInit() {
  this.api.getUsers().subscribe(users =&gt; this.userCount.set(users.length));
  this.api.getOrders().subscribe(orders =&gt; this.orderTotal.set(orders.length));
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;div&gt;Users: {{ userCount() }}&lt;/div&gt;
&lt;div&gt;Orders: {{ orderTotal() }}&lt;/div&gt;</code></pre>
</div>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p><strong>Signals update just what changed—no wasted DOM re-renders.</strong></p>
</div>
</blockquote>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_ssr_hydration_bonus_note">SSR &amp; Hydration (Bonus Note)</h3>
<div class="paragraph">
<p>💡 <em>SSR &amp; Hydration</em>: Angular 19’s hydration improvements are especially
useful if you’re rendering Angular on the server (Angular Universal).
Most projects won’t need this—but it’s a great step for future-proofing.</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_final_thoughts">Final Thoughts</h3>
<div class="paragraph">
<p>Angular 19 brings a more approachable and modern developer experience: signals simplify state, <code>@if</code> and <code>@for</code> make templates more readable, and <code>@defer</code> gives you fine control over performance.</p>
</div>
<div class="paragraph">
<p><strong>You just:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Replaced legacy form logic with clean Signals-based state.</p>
</li>
<li>
<p>Simplified templates using Angular’s new control flow.</p>
</li>
<li>
<p>Learned how to delay rendering and optimize performance with <code>@defer</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Next:</strong> We’ll bring everything together in Part 5 with route guards,
token parsing, and role-based access control.</p>
</div>
<hr>
<div class="sect3">
<h4 id="_further_reading">Further Reading</h4>
<div class="ulist">
<ul>
<li>
<p><a href="https://angular.dev">Angular 19 Signals Guide (angular.dev)</a></p>
</li>
<li>
<p><a href="https://angular.dev/reference/forms/using-signals">Reactive Forms
vs. Signals—How to Choose</a></p>
</li>
</ul>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Part 3: Frontend Setup with Angular</title>
		<link>https://blog.lunatech.com//posts/2025-05-06-part-3%3A-frontend-setup-with-angular</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-05-06T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[angular]]></category>
                }
             {
            <category><![CDATA[nestjs]]></category>
                }
             {
            <category><![CDATA[postgresql]]></category>
                }
             {
            <category><![CDATA[typeorm]]></category>
                }
             {
            <category><![CDATA[jwt]]></category>
                }
             {
            <category><![CDATA[authentication]]></category>
                }
             {
            <category><![CDATA[frontend]]></category>
                }
             {
            <category><![CDATA[typescript]]></category>
                }
             {
            <category><![CDATA[nodejs]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-05-06-part-3%3A-frontend-setup-with-angular</guid>

					<description>
                        <![CDATA[ Now that we’ve built our NestJS backend with secure JWT auth, it’s time]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Now that we’ve built our NestJS backend with secure JWT auth, it’s time
to wire up the frontend. In this part, we’ll scaffold an <strong>Angular</strong> app,
integrate it with our backend, and secure it with <strong>JWT-based route
protection</strong>.</p>
</div>
<div class="paragraph">
<p>By the end of this part, you’ll have:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A basic Angular app styled with Bootstrap</p>
</li>
<li>
<p>Auth service for login/register with JWT handling</p>
</li>
<li>
<p>Login and Register components using reactive forms</p>
</li>
<li>
<p>A protected Welcome page</p>
</li>
<li>
<p>Route guard to lock down private views</p>
</li>
</ul>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_step_1_initialize_the_angular_frontend"><strong>Step 1: Initialize the Angular Frontend</strong></h3>
<div class="paragraph">
<p>Let’s get your frontend up and running. Open your terminal and run:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ng new frontend
cd frontend</code></pre>
</div>
</div>
<div class="paragraph">
<p>Pick SCSS (or your favorite style option) when prompted.<br>
This gives you a fresh Angular workspace to play with.</p>
</div>
<div class="sect3">
<h4 id="_what_is_ng_in_angular_cli">What is <code>ng</code> in Angular CLI?</h4>
<div class="paragraph">
<p>The <code>ng</code> command is the core of the Angular CLI (Command Line Interface)
— a powerful toolkit that streamlines Angular development from start to
finish. It acts as a shortcut to perform a wide range of tasks without
manually setting up files or configurations.</p>
</div>
<div class="paragraph">
<p>With <code>ng</code>, you can quickly scaffold new projects, generate application
features, and manage your build and development workflow with ease. Here
are some common <code>ng</code> commands and what they do:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>ng new</code>: Creates a new Angular project with a complete directory
structure, TypeScript configuration, and ready-to-run setup.</p>
</li>
<li>
<p><code>ng generate</code> (or <code>ng g</code>): Automatically creates Angular building
blocks such as components, services, directives, pipes, and more —
following Angular’s style guide and best practices.</p>
</li>
<li>
<p><code>ng serve</code>: Launches a local development server with live reload. As
you make changes, the app updates instantly in the browser.</p>
</li>
<li>
<p><code>ng build</code>: Compiles your application into optimized static files for
production deployment.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The real power of <code>ng</code> lies in automation. It eliminates repetitive
boilerplate and helps you stick to Angular conventions, leading to
cleaner, more maintainable codebases.</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>💡 <strong>Tip:</strong> With Angular v19, components and services are created as
<strong>standalone</strong> by default. This means they no longer need to be declared
in NgModules, which promotes better modularity and reduces coupling in
your app architecture.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>By mastering the <code>ng</code> CLI, you’re not just saving time — you’re building
apps the Angular way, with a foundation rooted in scalability and
consistency.</p>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_install_dependencies"><strong>Step 2: Install Dependencies</strong></h3>
<div class="paragraph">
<p>We’ll need a few extras to make our app functional and visually
appealing:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Bootstrap</strong>: For responsive and professional styling.</p>
</li>
<li>
<p><strong>jwt-decode</strong>: To decode JWTs and extract user information.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Install them with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">npm install bootstrap jwt-decode</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_3_set_up_bootstrap"><strong>Step 3: Set Up Bootstrap</strong></h3>
<div class="paragraph">
<p>Let’s make things look good with minimal effort.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Open <code>angular.json</code></p>
</li>
<li>
<p>Find the <code>"styles"</code> array and add Bootstrap’s CSS:</p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-json" data-lang="json">"styles": [
  "node_modules/bootstrap/dist/css/bootstrap.min.css",
  "src/styles.scss"
]</code></pre>
</div>
</div>
</li>
<li>
<p>Start your development server if it’s not already running:</p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ng serve</code></pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>Test it by adding a Bootstrap class (like <code>btn btn-primary</code>) to a button
in <code>app.component.html</code>. If it’s blue, you’re golden.</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_4_setting_up_environments"><strong>Step 4: Setting Up Environments</strong></h3>
<div class="paragraph">
<p>Angular provides a built-in way to manage environment-specific
configurations, such as API URLs for development and production. Let’s
configure them.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Run the following command to generate the environment files for
development and production:</p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ng generate environments</code></pre>
</div>
</div>
<div class="paragraph">
<p>This will create two files in the <code>src/environments/</code> folder:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>src/
├── environments/
│   ├── environment.development.ts # Development environment
│   └── environment.ts             # Production environment</pre>
</div>
</div>
</li>
<li>
<p>Open both <code>environment.ts</code> and <code>environment.development.ts</code> and add
the <code>apiUrl</code> property. <code>environment.development.ts</code></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">export const environment = {
  production: false,
  apiUrl: "http://localhost:3000", // Replace with your backend's dev URL
};</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>environment.ts</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">export const environment = {
  production: true,
  apiUrl: "https://your-production-api.com", // Replace with your backend's prod URL
};</code></pre>
</div>
</div>
</li>
<li>
<p>The <code>environment</code> files are automatically selected based on the build
configuration:</p>
<div class="ulist">
<ul>
<li>
<p><strong>Development</strong>: When running <code>ng serve</code>, Angular replaces
<code>environment.ts</code> with <code>environment.development.ts</code>.</p>
</li>
<li>
<p><strong>Production</strong>: when running <code>ng build --prod</code>, Angular uses
<code>environment.ts</code>.</p>
<div class="paragraph">
<p>You can now use <code>environment.apiUrl</code> in your services to dynamically
switch between development and production APIs.</p>
</div>
</li>
</ul>
</div>
</li>
</ol>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_5_generate_guards_services_and_components"><strong>Step 5: Generate Guards, Services, and Components</strong></h3>
<div class="paragraph">
<p>Let Angular CLI do the heavy lifting for you. Run the following commands
to generate the necessary files:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">ng generate service services/auth
ng generate guard auth/auth
ng generate component pages/login
ng generate component pages/register
ng generate component pages/welcome</code></pre>
</div>
</div>
<div class="paragraph">
<p>What These Commands Do:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Auth Service</strong>: Handles API calls for login, registration, and token
management.</p>
</li>
<li>
<p><strong>Auth Guard</strong>: Protects routes by checking if the user is
authenticated.</p>
</li>
<li>
<p><strong>Login Component</strong>: Provides a form for users to log in.</p>
</li>
<li>
<p><strong>Register Component</strong>: Provides a form for users to register.</p>
</li>
<li>
<p><strong>Welcome Component</strong>: Displays a personalized welcome message for
logged-in users.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>This sets up your folders and boilerplate files, keeping things
organized and modular.</p>
</div>
<div class="sect3">
<h4 id="_project_structure_overview">Project Structure Overview</h4>
<div class="paragraph">
<p>Here’s how your Angular app should look after generating the files:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>src/
├── app/
│   ├── auth/                     # Guards, interceptors, maybe a module
│   │   └── auth.guard.ts
│   ├── pages/                    # Feature components
│   │   ├── login/
│   │   │   ├── login.component.html
│   │   │   ├── login.component.scss
│   │   │   └── login.component.ts
│   │   ├── register/
│   │   │   ├── register.component.html
│   │   │   ├── register.component.scss
│   │   │   └── register.component.ts
│   │   └── welcome/
│   │       ├── welcome.component.html
│   │       ├── welcome.component.scss
│   │       └── welcome.component.ts
│   ├── services/                 # API communication
│   │   └── auth.service.ts
│   ├── shared/                   # Models, utilities, etc. (optional)
│   ├── app.component.html
│   ├── app.component.scss
│   ├── app.component.ts
│   ├── app.config.ts
│   └── app.routes.ts
├── environments/
│   ├── environment.development.ts # Development environment
│   └── environment.ts             # Production environment
├── index.html
├── main.ts
└── style.scss</pre>
</div>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_step_6_the_auth_service_your_api_bridge"><strong>Step 6: The Auth Service – Your API Bridge</strong></h3>
<div class="paragraph">
<p>The <code>AuthService</code> is the backbone of your authentication flow. It
communicates with your backend’s <code>/auth/login</code> and <code>/auth/register</code>
endpoints, manages the JWT in localStorage, and provides utility methods
for token handling.</p>
</div>
<div class="paragraph">
<p>Here’s the complete implementation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/services/auth.service.ts
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { catchError, Observable, tap, throwError } from "rxjs";
import { environment } from "../../environments/environment";

@Injectable({
  providedIn: "root",
})
export class AuthService {
  constructor(private http: HttpClient) {}

  /**
   * Logs in the user by sending their credentials to the backend.
   * Stores the access token in localStorage upon success.
   * @param data - The user's email and password.
   * @returns An observable containing the access token.
   */
  login(data: {
    email: string;
    password: string;
  }): Observable&lt;{ access_token: string }&gt; {
    return this.http
      .post&lt;{ access_token: string }&gt;(`${environment.apiUrl}/auth/login`, data)
      .pipe(
        tap((res) =&gt; localStorage.setItem("access_token", res.access_token)),
        catchError((error) =&gt; {
          const errorMessage =
            error.status === 401
              ? "Invalid email or password. Please try again."
              : "An unexpected error occurred. Please try again later.";
          return throwError(() =&gt; new Error(errorMessage));
        })
      );
  }

  /**
   * Registers a new user by sending their details to the backend.
   * @param data - The user's name, email, password, and role.
   * @returns An observable for the registration process.
   */
  register(data: {
    email: string;
    password: string;
    name: string;
    role: string;
  }): Observable&lt;any&gt; {
    return this.http.post(`${environment.apiUrl}/auth/register`, data).pipe(
      catchError((error) =&gt; {
        const errorMessage =
          error.status === 400
            ? "Registration failed. Please check your input."
            : "An unexpected error occurred. Please try again later.";
        return throwError(() =&gt; new Error(errorMessage));
      })
    );
  }

  /**
   * Logs out the user by removing the access token from localStorage.
   */
  logout(): void {
    localStorage.removeItem("access_token");
  }

  /**
   * Retrieves the stored access token from localStorage.
   * @returns The access token or null if not found.
   */
  getToken(): string | null {
    return localStorage.getItem("access_token");
  }

  /**
   * Checks if the user is authenticated by verifying the presence of a token.
   * @returns A boolean indicating whether the user is authenticated.
   */
  isAuthenticated(): boolean {
    const token = this.getToken();
    return !!token;
  }
}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_7_protecting_routes_with_a_guard"><strong>Step 7: Protecting Routes with a Guard</strong></h3>
<div class="paragraph">
<p>Angular’s <code>CanActivate</code> guard is like a backend middleware for your
routes. Here’s how we check for a valid JWT:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/auth/auth.guard.ts
import { Injectable } from "@angular/core";
import { CanActivate, Router } from "@angular/router";
import { AuthService } from "../services/auth.service";
import { jwtDecode } from "jwt-decode";

@Injectable({ providedIn: "root" })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
    const token = this.authService.getToken();
    if (!token) {
      this.redirectToLogin();
      return false;
    }

    if (this.isTokenExpired(token)) {
      this.authService.logout();
      this.redirectToLogin();
      return false;
    }

    return true;
  }

  /**
   * Checks if the token is expired.
   * @param token - The JWT token to validate.
   * @returns A boolean indicating whether the token is expired.
   */
  private isTokenExpired(token: string): boolean {
    try {
      const decoded: any = jwtDecode(token);
      return Date.now() &gt; decoded.exp * 1000;
    } catch {
      return true; // Treat invalid tokens as expired
    }
  }

  /**
   * Redirects the user to the login page.
   */
  private redirectToLogin(): void {
    this.router.navigate(["/login"]);
  }
}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_8_login_register_components"><strong>Step 8: Login &amp; Register Components</strong></h3>
<div class="paragraph">
<p>The <code>LoginComponent</code> and <code>RegisterComponent</code> are the core components for
user authentication in the application. Both components use Angular’s
reactive forms to manage user input and validations. They interact with
the <code>AuthService</code> to send requests to the backend for login and
registration functionality. Upon successful operations, they navigate
the user to the appropriate page (<code>/welcome</code> for login and <code>/login</code> for
registration).</p>
</div>
<div class="paragraph">
<p>Key Features</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Reactive Forms</strong>: Both components use Angular’s reactive forms to
handle user input and validations.</p>
</li>
<li>
<p><strong>Validation Rules</strong>: Fields like <code>email</code>, <code>password</code>, and <code>name</code> (for
registration) have validation rules to ensure proper input.</p>
</li>
<li>
<p><strong>Error Handling</strong>: User-friendly error messages are displayed when
login or registration fails.</p>
</li>
<li>
<p><strong>Loading State</strong>: Prevents duplicate submissions by disabling the
submit button while the request is in progress.</p>
</li>
<li>
<p><strong>Navigation</strong>: Redirects users to the appropriate page upon successful
login or registration.</p>
</li>
</ul>
</div>
<hr>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Login Component</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/pages/login/login.component.ts
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import {
  FormBuilder,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from "@angular/forms";
import { AuthService } from "../../services/auth.service";
import { CommonModule } from "@angular/common";

@Component({
  selector: "app-login",
  templateUrl: "./login.component.html",
  styleUrls: ["./login.component.scss"],
  imports: [ReactiveFormsModule, CommonModule],
})
export class LoginComponent {
  form: FormGroup;
  isLoading = false;
  errorMessage: string | null = null;

  constructor(
    private fb: FormBuilder,
    private auth: AuthService,
    private router: Router
  ) {
    this.form = this.fb.group({
      email: ["", [Validators.required, Validators.email]],
      password: ["", [Validators.required, Validators.minLength(6)]],
    });
  }

  onSubmit() {
    if (this.form.invalid) return;

    this.isLoading = true;
    this.errorMessage = null;

    this.auth.login(this.form.value).subscribe({
      next: () =&gt; {
        this.isLoading = false;
        this.router.navigate(["/welcome"]);
      },
      error: (err) =&gt; {
        this.isLoading = false;
        this.errorMessage =
          err.error?.message || "Login failed. Please try again.";
      },
    });
  }
}</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Login HTML</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;!-- src/app/pages/login/login.component.html --&gt;
&lt;form
  [formGroup]="form"
  (ngSubmit)="onSubmit()"
  class="mt-4 p-4 border rounded shadow-sm bg-white"
  style="max-width: 400px; margin: auto"
&gt;
  &lt;h2 class="text-center mb-4"&gt;Login&lt;/h2&gt;

  &lt;div class="mb-3"&gt;
    &lt;input
      formControlName="email"
      type="email"
      class="form-control"
      placeholder="Email"
      [class.is-invalid]="
        form.get('email')?.invalid &amp;&amp; form.get('email')?.touched
      "
      aria-label="Email"
    /&gt;
    &lt;div
      *ngIf="form.get('email')?.invalid &amp;&amp; form.get('email')?.touched"
      class="invalid-feedback"
    &gt;
      Please enter a valid email.
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="mb-3"&gt;
    &lt;input
      formControlName="password"
      type="password"
      class="form-control"
      placeholder="Password"
      [class.is-invalid]="
        form.get('password')?.invalid &amp;&amp; form.get('password')?.touched
      "
      aria-label="Password"
    /&gt;
    &lt;div
      *ngIf="form.get('password')?.invalid &amp;&amp; form.get('password')?.touched"
      class="invalid-feedback"
    &gt;
      Password must be at least 6 characters long.
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div *ngIf="errorMessage" class="alert alert-danger"&gt;
    {{ errorMessage }}
  &lt;/div&gt;

  &lt;button type="submit" class="btn btn-primary w-100" [disabled]="isLoading"&gt;
    &lt;span
      *ngIf="isLoading"
      class="spinner-border spinner-border-sm me-2"
    &gt;&lt;/span&gt;
    Login
  &lt;/button&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Register Component</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/pages/register/register.component.ts
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import {
  FormBuilder,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from "@angular/forms";
import { AuthService } from "../../services/auth.service";
import { CommonModule } from "@angular/common";

@Component({
  selector: "app-register",
  templateUrl: "./register.component.html",
  styleUrls: ["./register.component.scss"],
  imports: [ReactiveFormsModule, CommonModule],
})
export class RegisterComponent {
  form: FormGroup;
  isLoading = false;
  errorMessage: string | null = null;

  constructor(
    private fb: FormBuilder,
    private auth: AuthService,
    private router: Router
  ) {
    this.form = this.fb.group({
      name: ["", [Validators.required]],
      email: ["", [Validators.required, Validators.email]],
      password: ["", [Validators.required, Validators.minLength(6)]],
      role: ["user", [Validators.required]],
    });
  }

  onSubmit() {
    if (this.form.invalid) return;

    this.isLoading = true;
    this.errorMessage = null;

    this.auth.register(this.form.value).subscribe({
      next: () =&gt; {
        this.isLoading = false;
        this.router.navigate(["/login"]);
      },
      error: (err) =&gt; {
        this.isLoading = false;
        this.errorMessage =
          err.error?.message || "Registration failed. Please try again.";
      },
    });
  }
}</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Register HTML</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;form
  [formGroup]="form"
  (ngSubmit)="onSubmit()"
  class="mt-4 p-4 border rounded shadow-sm bg-white"
  style="max-width: 400px; margin: auto"
&gt;
  &lt;h2 class="text-center mb-4"&gt;Register&lt;/h2&gt;

  &lt;div class="mb-3"&gt;
    &lt;input
      formControlName="name"
      type="text"
      class="form-control"
      placeholder="Name"
      [class.is-invalid]="
         form.get('name')?.invalid &amp;&amp; form.get('name')?.touched
       "
      aria-label="Name"
    /&gt;
    &lt;div
      *ngIf="form.get('name')?.invalid &amp;&amp; form.get('name')?.touched"
      class="invalid-feedback"
    &gt;
      Name is required.
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="mb-3"&gt;
    &lt;input
      formControlName="email"
      type="email"
      class="form-control"
      placeholder="Email"
      [class.is-invalid]="
         form.get('email')?.invalid &amp;&amp; form.get('email')?.touched
       "
      aria-label="Email"
    /&gt;
    &lt;div
      *ngIf="form.get('email')?.invalid &amp;&amp; form.get('email')?.touched"
      class="invalid-feedback"
    &gt;
      Please enter a valid email.
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="mb-3"&gt;
    &lt;input
      formControlName="password"
      type="password"
      class="form-control"
      placeholder="Password"
      [class.is-invalid]="
         form.get('password')?.invalid &amp;&amp; form.get('password')?.touched
       "
      aria-label="Password"
    /&gt;
    &lt;div
      *ngIf="form.get('password')?.invalid &amp;&amp; form.get('password')?.touched"
      class="invalid-feedback"
    &gt;
      Password must be at least 6 characters long.
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div *ngIf="errorMessage" class="alert alert-danger"&gt;
    {{ errorMessage }}
  &lt;/div&gt;

  &lt;button type="submit" class="btn btn-success w-100" [disabled]="isLoading"&gt;
    &lt;span
      *ngIf="isLoading"
      class="spinner-border spinner-border-sm me-2"
    &gt;&lt;/span&gt;
    Register
  &lt;/button&gt;
&lt;/form&gt;</code></pre>
</div>
</div>
</li>
</ol>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_9_welcome_page_static_greeting_and_logout"><strong>Step 9: Welcome Page – Static Greeting and Logout</strong></h3>
<div class="paragraph">
<p>The <code>WelcomeComponent</code> provides a user-friendly page that welcomes the
user after a successful login. It integrates with the existing
AuthService to manage logout functionality but does not attempt to
decode or extract any user-specific data from the access token.</p>
</div>
<div class="paragraph">
<p>Key Features</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Static Greeting</strong>: Display a generic welcome message for all users.</p>
</li>
<li>
<p><strong>Logout Functionality</strong>: Allow users to log out and clear their
session.</p>
</li>
</ul>
</div>
<hr>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Welcome Component</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/pages/welcome/welcome.component.ts
import { Component } from "@angular/core";
import { AuthService } from "../../services/auth.service";
import { Router } from "@angular/router";

@Component({
  selector: "app-welcome",
  templateUrl: "./welcome.component.html",
  styleUrls: ["./welcome.component.scss"],
})
export class WelcomeComponent {
  constructor(private authService: AuthService, private router: Router) {}

  logout(): void {
    this.authService.logout();
    this.router.navigate(["/login"]);
  }
}</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Welcome HTML</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-html" data-lang="html">&lt;!-- src/app/pages/welcome/welcome.component.html --&gt;
&lt;div class="welcome-container text-center mt-5"&gt;
  &lt;h1 class="display-4"&gt;Welcome!&lt;/h1&gt;
  &lt;p class="lead"&gt;We're glad to have you here.&lt;/p&gt;
  &lt;button class="btn btn-primary mt-3" (click)="logout()"&gt;Logout&lt;/button&gt;
&lt;/div&gt;</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Welcome Component SCSS</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scss" data-lang="scss">/* /src/app/pages/welcome/welcome.component.scss */
.welcome-container {
  max-width: 600px;
  margin: auto;
  padding: 20px;
  background-color: #f8f9fa;
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

h1 {
  color: #343a40;
}

p {
  color: #6c757d;
}</code></pre>
</div>
</div>
</li>
</ol>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_10_routing_lock_down_protected_pages"><strong>Step 10: Routing: Lock Down Protected Pages</strong></h3>
<div class="paragraph">
<p>Alright, now that we’ve got our login, registration, and welcome pages
ready, it’s time to lock things down. We don’t want just anyone
accessing the protected parts of our app, right? That’s where Angular’s
routing and guards come into play. Let’s set up our routes and ensure
only authenticated users can access the welcome page.</p>
</div>
<div class="sect3">
<h4 id="_setting_up_routes">Setting Up Routes</h4>
<div class="paragraph">
<p>In Angular, routes define how users navigate through your app. Here’s
how we’ll structure our routes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>/login</code>: For users to log in.</p>
</li>
<li>
<p><code>/register</code>: For new users to sign up.</p>
</li>
<li>
<p><code>/welcome</code>: A protected page that greets logged-in users.</p>
</li>
<li>
<p><code>/</code>: Redirects to <code>/welcome</code> by default.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Here’s the code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/app/app.routes.ts
import { Routes } from "@angular/router";
import { LoginComponent } from "./pages/login/login.component";
import { RegisterComponent } from "./pages/register/register.component";
import { WelcomeComponent } from "./pages/welcome/welcome.component";
import { AuthGuard } from "./auth/auth.guard";

export const routes: Routes = [
  { path: "", redirectTo: "welcome", pathMatch: "full" },
  { path: "login", component: LoginComponent },
  { path: "register", component: RegisterComponent },
  { path: "welcome", component: WelcomeComponent, canActivate: [AuthGuard] },
];</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is a simple and clean way to define your app’s navigation. Notice
how we’ve added <code>canActivate: [AuthGuard]</code> to the <code>/welcome</code> route?
That’s the magic that protects it.</p>
</div>
</div>
<div class="sect3">
<h4 id="_protecting_routes_with_a_guard">Protecting Routes with a Guard</h4>
<div class="paragraph">
<p>The <code>AuthGuard</code> is like a bouncer for your routes. It checks if the user
is authenticated before letting them in. If they’re not, it redirects
them to the login page.</p>
</div>
</div>
<div class="sect3">
<h4 id="_wiring_it_all_together">Wiring It All Together</h4>
<div class="paragraph">
<p>Now that we’ve defined our routes and guard, let’s hook everything up in
<code>main.ts</code>. Angular v19 makes this super simple with the <code>provideRouter</code>
function:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-typescript" data-lang="typescript">// src/main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
import { provideRouter } from "@angular/router";
import { routes } from "./app/app.routes";
import { provideHttpClient } from "@angular/common/http";

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes), // Provide the routes
    provideHttpClient(), // Provide the HTTP client
  ],
}).catch((err) =&gt; console.error(err));</code></pre>
</div>
</div>
<div class="paragraph">
<p>No need for a separate <code>AppRoutingModule</code>. Just provide the routes
directly in <code>main.ts</code>, and you’re good to go.</p>
</div>
</div>
<div class="sect3">
<h4 id="_testing_it_out">Testing It Out</h4>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Try Accessing <code>/welcome</code> Without Logging In</strong>:</p>
<div class="ulist">
<ul>
<li>
<p>You should be redirected to <code>/login</code>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Log In and Access <code>/welcome</code></strong>:</p>
<div class="ulist">
<ul>
<li>
<p>After logging in, navigate to <code>/welcome</code>. You should see the
personalized greeting.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Log Out and Try Again</strong>:</p>
<div class="ulist">
<ul>
<li>
<p>After logging out, try accessing <code>/welcome</code> again. You should be
redirected to <code>/login</code>.</p>
</li>
</ul>
</div>
</li>
</ol>
</div>
</div>
<div class="sect3">
<h4 id="_why_this_matters">Why This Matters</h4>
<div class="paragraph">
<p>For backend developers, this should feel familiar. Think of the
<code>AuthGuard</code> as middleware for your routes. It ensures that only
authenticated users can access certain parts of your app, just like how
you’d protect API endpoints on the server side.</p>
</div>
<div class="paragraph">
<p>By combining Angular’s routing with guards, you’ve got a solid
foundation for building secure, user-friendly apps. And the best part?
It’s modular and easy to extend. Want to add role-based access? Just
tweak the guard. Need to protect more routes? Add <code>canActivate</code> to them.</p>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_whats_next"><strong>What’s Next?</strong></h3>
<div class="paragraph">
<p>You’ve got a working Angular app wired to your backend, protected with
route guards and powered by JWT auth. Next up, we’ll level things up
with some Angular 19 enhancements and best practices.</p>
</div>
<div class="paragraph">
<p>👉 <strong>Part 4: Angular 19 Deep Dive</strong></p>
</div>
<div class="paragraph">
<p>We’ll cover:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>🧠 <strong>Signals vs Observables</strong> — When to use which and why</p>
</li>
<li>
<p>🔁 <code>@if</code>, <code>@for</code>, and <code>defer</code> blocks — Smarter templates with better
control</p>
</li>
<li>
<p>⚡ <strong>Change detection and performance tuning</strong></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This part’s optional, but worth it if you want your Angular app to be
leaner, faster, and future-ready.</p>
</div>
<div class="paragraph">
<p>For now, pat yourself on the back—you’ve just implemented a secure,
modern routing system in Angular. 🚀</p>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>AI plugins for code development</title>
		<link>https://blog.lunatech.com//posts/2025-05-02-ai-plugins-for-code-development</link>
		
		<dc:creator><![CDATA[Rajendra Maniyal]]></dc:creator>
        <pubDate>2025-05-02T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[AI]]></category>
                }
             {
            <category><![CDATA[IntelliJ AI Plugins]]></category>
                }
             {
            <category><![CDATA[Local LLMs]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-05-02-ai-plugins-for-code-development</guid>

					<description>
                        <![CDATA[ Recently, I experimented with integrating AI into my IntelliJ IDEA setup for JVM development, and I wanted to share the steps I followed. It turned out to be really helpful, so maybe this guide can assist someone else in getting started too!]]></description>
                    <content:encoded><![CDATA[
                    <div class="sect1">
<h2 id="_introduction">Introduction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Recently, I experimented with integrating AI into my IntelliJ IDEA setup for JVM development, and I wanted to share the steps I followed. It turned out to be really helpful, so maybe this guide can assist someone else in getting started too!</p>
</div>
<div class="paragraph">
<p>IntelliJ is one of the preferred IDE for Java, Scala and Kotlin development. I have been using the IntelliJ IDEA with plugins for AI, which allows me to use Local LLMs to generate code suggestions, refactor my code and even write tests for me.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_what_i_did">What I did</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong>Step 1: Install Ollama</strong></p>
</div>
<div class="paragraph">
<p>First, visit <a href="https://ollama.com/">Ollama</a> and download the version for your operating system. Follow the simple installation steps.</p>
</div>
<div class="paragraph">
<p>Open your terminal and run <code>ollama pull llama3</code> to download the model. Start the model by running <code>ollama serve llama3</code>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">Tip</div>
</td>
<td class="content">
Choosing the right model is very important .
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p><strong>Step 2: IntelliJ Plugins</strong></p>
</div>
<div class="paragraph">
<p>I tested a few plugins and found Continue.dev really useful:</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
A list of plugins at the bottom of this article with links
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Continue.dev
Helps with code completion, converting comments into code, and debugging tips.
Works locally (privacy-friendly), quick.
Needs decent hardware resources.</p>
</div>
<div class="paragraph">
<p><strong>Step 3: Setting Up Continue.dev with IntelliJ</strong></p>
</div>
<div class="paragraph">
<p>Install the Plugin:
Open IntelliJ IDEA, navigate to Settings &#8594; Plugins, search for "Continue.dev", and install it.</p>
</div>
<div class="paragraph">
<p>Connect to Ollama:
Open the Continue.dev settings and add the local Ollama server URL (<code><a href="http://localhost:11434" class="bare">http://localhost:11434</a></code>).</p>
</div>
<div class="paragraph">
<p><strong>Step 4: Exploring AI Features</strong></p>
</div>
<div class="paragraph">
<p>Code Suggestions:
The plugin provided accurate code completions immediately, significantly speeding up my workflow.</p>
</div>
<div class="paragraph">
<p>Generating Code from Comments:
I tried writing simple English comments, and Continue.dev translated them into functioning code snippets effortlessly.</p>
</div>
<div class="paragraph">
<p>Debugging:
Even when intentionally making small errors, the plugin quickly suggested effective fixes.</p>
</div>
<div class="paragraph">
<p><strong>Step 5: AI-Assisted Refactoring</strong></p>
</div>
<div class="paragraph">
<p>I highlighted some messy code.
Using the AI suggestions via right-click helped clean up and optimize the code efficiently.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_tips_for_best_results">Tips for Best Results:</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Regularly update plugins and AI models for optimal performance.</p>
</div>
<div class="paragraph">
<p>If hardware resources are limited, experiment with smaller models initially.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_intellij_idea_plugins_supporting_local_llm_integration">IntelliJ IDEA Plugins Supporting Local LLM Integration</h2>
<div class="sectionbody">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 14.2857%;">
<col style="width: 28.5714%;">
<col style="width: 28.5714%;">
<col style="width: 28.5715%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Plugin</th>
<th class="tableblock halign-left valign-top">Features</th>
<th class="tableblock halign-left valign-top">Pros</th>
<th class="tableblock halign-left valign-top">Cons</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong><a href="https://plugins.jetbrains.com/plugin/22707-continue">Continue.dev</a></strong></p></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="ulist">
<ul>
<li>
<p>Code completion</p>
</li>
<li>
<p>Natural language to code generation</p>
</li>
<li>
<p>Inline code explanations</p>
</li>
<li>
<p>Supports multiple local LLM providers</p>
</li>
</ul>
</div></div></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- Open-source
- Highly customizable
- Supports various local LLMs like Ollama, LM Studio, GPT4All</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- May require manual configuration for optimal performance</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong><a href="https://plugins.jetbrains.com/plugin/24169-devoxxgenie">DevoxxGenie</a></strong></p></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="ulist">
<ul>
<li>
<p>Code review and explanation</p>
</li>
<li>
<p>Unit test generation</p>
</li>
<li>
<p>Retrieval-Augmented Generation (RAG)</p>
</li>
<li>
<p>Integration with various local LLMs</p>
</li>
</ul>
</div></div></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- Fully Java-based
- Rich feature set including RAG and Git Diff viewer
- Supports multiple local LLMs</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- Interface may be overwhelming for beginners</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong><a href="https://plugins.jetbrains.com/plugin/21056-proxy-ai">Proxy AI</a></strong></p></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="ulist">
<ul>
<li>
<p>Connects IntelliJ IDEA with locally running LLMs</p>
</li>
<li>
<p>Provides AI assistance features</p>
</li>
</ul>
</div></div></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- Open-source
- Simple setup for local LLM integration</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- Limited feature set compared to other plugins</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong><a href="https://plugins.jetbrains.com/plugin/24372-codegpt-chat&#8212;&#8203;ai-agents">CodeGPT</a></strong></p></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="ulist">
<ul>
<li>
<p>AI-powered code suggestions</p>
</li>
<li>
<p>Chat interface within IntelliJ IDEA</p>
</li>
<li>
<p>Supports local LLMs via LM Studio</p>
</li>
</ul>
</div></div></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- User-friendly interface
- Supports multiple programming languages</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- May require additional setup for local LLM integration</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong><a href="https://plugins.jetbrains.com/plugin/22282-jetbrains-ai-assistant">JetBrains AI Assistant</a></strong></p></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="ulist">
<ul>
<li>
<p>AI-driven code suggestions</p>
</li>
<li>
<p>Code explanations and documentation generation</p>
</li>
<li>
<p>Integration with local LLMs</p>
</li>
</ul>
</div></div></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- Official JetBrains plugin
- Seamless integration with IntelliJ IDEA</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">- May have limited customization compared to open-source alternatives</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Part 2: Backend Setup with NestJS</title>
		<link>https://blog.lunatech.com//posts/2025-04-22-part-2%3A-backend-setup-with-nestjs</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-04-22T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[angular]]></category>
                }
             {
            <category><![CDATA[nestjs]]></category>
                }
             {
            <category><![CDATA[postgresql]]></category>
                }
             {
            <category><![CDATA[typeorm]]></category>
                }
             {
            <category><![CDATA[jwt]]></category>
                }
             {
            <category><![CDATA[authentication]]></category>
                }
             {
            <category><![CDATA[frontend]]></category>
                }
             {
            <category><![CDATA[typescript]]></category>
                }
             {
            <category><![CDATA[nodejs]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-04-22-part-2%3A-backend-setup-with-nestjs</guid>

					<description>
                        <![CDATA[ With our project vision and stack laid out in Part 1, it’s time to build the]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>With our project vision and stack laid out in Part 1, it’s time to build the
backend.
In this part, we’ll scaffold a <strong>NestJS</strong> project, connect it to <strong>PostgreSQL</strong>
via <strong>TypeORM</strong>, and implement secure <strong>JWT-based authentication</strong>.</p>
</div>
<div class="paragraph">
<p>By the end of this part, you’ll have:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A working NestJS app connected to PostgreSQL</p>
</li>
<li>
<p>A <code>User</code> entity with hashed passwords</p>
</li>
<li>
<p>Endpoints for registration and login</p>
</li>
<li>
<p>JWT authentication using guards and strategies</p>
</li>
<li>
<p>CORS enabled to allow Angular frontend communication</p>
</li>
</ul>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_project_structure_overview"><strong>Project Structure Overview</strong></h3>
<div class="paragraph">
<p>Once set up, your backend folder will look like this:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>src/
├── app.module.ts
├── main.ts
├── auth/
│   ├── auth.controller.ts
│   ├── auth.module.ts
│   ├── auth.service.ts
│   ├── dto/
│   │   ├── login.dto.ts
│   │   └── register.dto.ts
│   └── jwt/
│       └── jwt.strategy.ts
├── users/
│   ├── user.entity.ts
│   ├── users.controller.ts
│   ├── users.module.ts
│   └── users.service.ts
.env</pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_goals_for_part_2"><strong>Goals for Part 2</strong></h3>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Scaffold a NestJS project with PostgreSQL and TypeORM</p>
</li>
<li>
<p>Create a <code>User</code> entity, DTOs with validation, and an Auth module</p>
</li>
<li>
<p>Implement registration and login with password hashing</p>
</li>
<li>
<p>Add JWT authentication with guards and strategy</p>
</li>
<li>
<p>Enable CORS for frontend connectivity</p>
</li>
</ol>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_1_run_postgresql_and_set_environment_variables"><strong>Step 1: Run PostgreSQL and Set Environment Variables</strong></h3>
<div class="paragraph">
<p>You can use either Docker or Podman.
Run <strong>one</strong> of the following commands in your terminal:</p>
</div>
<div class="paragraph">
<p><strong>Docker:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">docker run --name pg-nest \
  -e POSTGRES_USER=nestuser \
  -e POSTGRES_PASSWORD=nestpass \
  -e POSTGRES_DB=nestdb \
  -p 5432:5432 \
  -d postgres</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Podman:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">podman run --name pg-nest \
  -e POSTGRES_USER=nestuser \
  -e POSTGRES_PASSWORD=nestpass \
  -e POSTGRES_DB=nestdb \
  -p 5432:5432 \
  -d postgres</code></pre>
</div>
</div>
<div class="paragraph">
<p>Once the project is created in Step 2, add a <code>.env</code> file to your root:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>DB_HOST=localhost DB_PORT=5432 DB_USERNAME=nestuser DB_PASSWORD=nestpass
DB_NAME=nestdb JWT_SECRET=dev_secret BCRYPT_SALT_ROUNDS=10</pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_2_scaffold_the_project_and_install_dependencies"><strong>Step 2: Scaffold the Project and Install Dependencies</strong></h3>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">nest new backend
cd backend</code></pre>
</div>
</div>
<div class="paragraph">
<p>Install project dependencies:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">npm install @nestjs/typeorm typeorm pg @nestjs/jwt passport-jwt @nestjs/passport bcrypt class-validator class-transformer dotenv</code></pre>
</div>
</div>
<div class="paragraph">
<p>At the top of <code>main.ts</code>, import environment variables:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import "dotenv/config";</code></pre>
</div>
</div>
<div class="paragraph">
<p>Enable CORS:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">app.enableCors({
  origin: "http://localhost:4200",
  credentials: true,
});</code></pre>
</div>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>✅ <strong>Tip</strong>: For production, consider using <code>@nestjs/config</code> for cleaner
environment management.</p>
</div>
</blockquote>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_3_configure_typeorm"><strong>Step 3: Configure TypeORM</strong></h3>
<div class="paragraph">
<p>In <code>app.module.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { UsersModule } from "./users/users.module";
import { AuthModule } from "./auth/auth.module";

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: "postgres",
      host: process.env.DB_HOST,
      port: parseInt(process.env.DB_PORT, 10),
      username: process.env.DB_USERNAME,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      entities: [__dirname + "/**/*.entity{.ts,.js}"],
      synchronize: true, // ⚠️ Don't use this in production — it'll drop/recreate tables
    }),
    UsersModule,
    AuthModule,
  ],
})
export class AppModule {}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_4_create_the_user_module_and_entity"><strong>Step 4: Create the User Module and Entity</strong></h3>
<div class="paragraph">
<p>Generate boilerplate:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">nest g module users
nest g service users
nest g controller users</code></pre>
</div>
</div>
<div class="paragraph">
<p>In <code>users/user.entity.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Exclude } from "class-transformer";
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  email: string;

  @Column()
  name: string;

  @Column()
  @Exclude()
  password: string;

  @Column()
  role: string;
}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_5_set_up_the_auth_module"><strong>Step 5: Set Up the Auth Module</strong></h3>
<div class="paragraph">
<p>Generate files:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">nest g module auth
nest g service auth
nest g controller auth</code></pre>
</div>
</div>
<div class="sect3">
<h4 id="_step_5a_create_dtos_with_validation">Step 5a: Create DTOs with Validation</h4>
<div class="paragraph">
<p>In <code>auth/dto/register.dto.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { IsEmail, IsNotEmpty, MinLength } from "class-validator";

export class RegisterDto {
  @IsEmail()
  email: string;

  @MinLength(6)
  password: string;

  @IsNotEmpty()
  name: string;

  @IsNotEmpty()
  role: string;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>These are like your request payload models with annotations.
Think <code>@NotEmpty</code>, <code>@Email</code>, etc.
The validation logic is handled globally (we’ll wire that up in <code>main.ts</code>
using <code>ValidationPipe</code>).</p>
</div>
<div class="paragraph">
<p>In <code>auth/dto/login.dto.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { IsEmail, MinLength } from "class-validator";

export class LoginDto {
  @IsEmail()
  email: string;

  @MinLength(6)
  password: string;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Enable validation globally in <code>main.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { ValidationPipe } from "@nestjs/common";

app.useGlobalPipes(new ValidationPipe({ whitelist: true }));</code></pre>
</div>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_step_6_implement_usersservice"><strong>Step 6: Implement UsersService</strong></h3>
<div class="paragraph">
<p>This service handles persistence logic using TypeORM’s repository pattern.
In <code>users.service.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./user.entity";

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private repo: Repository&lt;User&gt;
  ) {}

  create(data: Partial&lt;User&gt;) {
    const user = this.repo.create(data);
    return this.repo.save(user);
  }

  findByEmail(email: string) {
    return this.repo.findOne({ where: { email } });
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Again, if you’re used to JPA, this is just standard repository stuff.</p>
</div>
<div class="paragraph">
<p>In <code>users.module.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./user.entity";
import { UsersService } from "./users.service";
import { UsersController } from "./users.controller";

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  providers: [UsersService],
  controllers: [UsersController],
  exports: [UsersService],
})
export class UsersModule {}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_7_build_authservice"><strong>Step 7: Build AuthService</strong></h3>
<div class="paragraph">
<p>This is where registration and login happens.
We hash passwords with <code>bcrypt</code>, and return a JWT if login succeeds.</p>
</div>
<div class="paragraph">
<p>You’ll notice:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">delete user.password;</code></pre>
</div>
</div>
<div class="paragraph">
<p>It manually removes the password before returning the user — feels a bit
hacky, but we’ve also used <code>@Exclude()</code> in the entity, so this is just being
extra cautious.</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>⚠️ We’re using both <code>@Exclude()</code> (to hide the password when transforming
entities) and <code>delete user.password</code> as a backup.
Depending on how Nest returns the object — directly vs. through a
serialization step — the password field might still leak through without
this extra guard.</p>
</div>
</blockquote>
</div>
<div class="paragraph">
<p>In <code>auth.service.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Injectable, UnauthorizedException } from "@nestjs/common";
import * as bcrypt from "bcrypt";
import { JwtService } from "@nestjs/jwt";
import { UsersService } from "../users/users.service";
import { RegisterDto } from "./dto/register.dto";
import { LoginDto } from "./dto/login.dto";

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService
  ) {}

  async register(dto: RegisterDto) {
    const existing = await this.usersService.findByEmail(dto.email);
    if (existing) throw new UnauthorizedException("Email already in use");

    const hashed = await bcrypt.hash(dto.password, 10);
    const user = await this.usersService.create({
      ...dto,
      password: hashed,
    });
    delete user.password;
    return user;
  }

  async login(dto: LoginDto) {
    const user = await this.usersService.findByEmail(dto.email);
    const valid = user &amp;&amp; (await bcrypt.compare(dto.password, user.password));
    if (!valid) throw new UnauthorizedException("Invalid credentials");

    const payload = { sub: user.id, role: user.role };
    return { access_token: this.jwtService.sign(payload) };
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In <code>auth.module.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Module } from "@nestjs/common";
import { JwtModule } from "@nestjs/jwt";
import { AuthService } from "./auth.service";
import { AuthController } from "./auth.controller";
import { UsersModule } from "../users/users.module";
import { JwtStrategy } from "./jwt/jwt.strategy";

@Module({
  imports: [
    UsersModule,
    JwtModule.register({
      secret: process.env.JWT_SECRET,
      signOptions: { expiresIn: "1d" },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
})
export class AuthModule {}</code></pre>
</div>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_8_jwt_strategy_and_guards"><strong>Step 8: JWT Strategy and Guards</strong></h3>
<div class="paragraph">
<p>Here we set up Passport’s JWT strategy.
If you’re new to Passport: it’s just NestJS’s way of plugging in different
auth strategies.</p>
</div>
<div class="paragraph">
<p>Create the strategy file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">touch src/auth/jwt/jwt.strategy.ts</code></pre>
</div>
</div>
<div class="paragraph">
<p>In <code>jwt.strategy.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_SECRET,
    });
  }

  validate(payload: any) {
    return { id: payload.sub, role: payload.role };
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This function runs once the JWT is verified.
It attaches the returned object to <code>req.user</code>.</p>
</div>
<div class="paragraph">
<p>So if you hit a route with a valid JWT, this is what gets injected.</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_9_connect_auth_routes"><strong>Step 9: Connect Auth Routes</strong></h3>
<div class="paragraph">
<p>In <code>auth.controller.ts</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ts" data-lang="ts">import { Controller, Post, Body } from "@nestjs/common";
import { AuthService } from "./auth.service";
import { RegisterDto } from "./dto/register.dto";
import { LoginDto } from "./dto/login.dto";

@Controller("auth")
export class AuthController {
  constructor(private authService: AuthService) {}

  @Post("register")
  register(@Body() dto: RegisterDto) {
    return this.authService.register(dto);
  }

  @Post("login")
  login(@Body() dto: LoginDto) {
    return this.authService.login(dto);
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Two routes here:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>POST /auth/register</code> → Creates a user</p>
</li>
<li>
<p><code>POST /auth/login</code> → Validates user and returns a token</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Nice and clean.</p>
</div>
<hr>
</div>
<div class="sect2">
<h3 id="_step_10_test_it"><strong>Step 10: Test It</strong></h3>
<div class="paragraph">
<p>Start your server:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">npm run start:dev</code></pre>
</div>
</div>
<div class="paragraph">
<p>Use Postman or Insomnia:</p>
</div>
<div class="sect3">
<h4 id="_register">Register</h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-http" data-lang="http">POST /auth/register
Content-Type: application/json

{
  "email": "test@example.com",
  "password": "123456",
  "name": "Test User",
  "role": "user"
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_login">Login</h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-http" data-lang="http">POST /auth/login
Content-Type: application/json

{
  "email": "test@example.com",
  "password": "123456"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Response:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-json" data-lang="json">{
  "access_token": "&lt;JWT_TOKEN&gt;"
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Use this token to access protected routes with:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>Authorization: Bearer &lt;JWT_TOKEN&gt;</pre>
</div>
</div>
<hr>
</div>
</div>
<div class="sect2">
<h3 id="_recap"><strong>Recap</strong></h3>
<div class="paragraph">
<p>You now have:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A functional NestJS backend with PostgreSQL</p>
</li>
<li>
<p>User registration and login with secure hashed passwords</p>
</li>
<li>
<p>JWT-based authentication strategy and guards</p>
</li>
<li>
<p>DTO validation and global pipes</p>
</li>
<li>
<p>Environment config and CORS enabled for the frontend</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>👉 <strong>Next up (Part 3): We’ll switch gears and start building the Angular
frontend — hooking it up to this backend, wiring in JWT auth, and securing
client-side routes.</strong></p>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>GenAI: Optimizing Local Large Language Models Performance</title>
		<link>https://blog.lunatech.com//posts/2025-04-17-genai%3A-optimizing-local-large-language-models-performance</link>
		
		<dc:creator><![CDATA[Radek Kargul]]></dc:creator>
        <pubDate>2025-04-17T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[gen-ai]]></category>
                }
             {
            <category><![CDATA[llm]]></category>
                }
             {
            <category><![CDATA[generative-ai]]></category>
                }
             {
            <category><![CDATA[local-llm]]></category>
                }
             {
            <category><![CDATA[ollama]]></category>
                }
             {
            <category><![CDATA[quantization]]></category>
                }
             {
            <category><![CDATA[model-optimization]]></category>
                }
             {
            <category><![CDATA[neural-networks]]></category>
                }
             {
            <category><![CDATA[model-inference]]></category>
                }
             {
            <category><![CDATA[open-source]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-04-17-genai%3A-optimizing-local-large-language-models-performance</guid>

					<description>
                        <![CDATA[ Local large language models (LLMs) are becoming more capable and accessible than ever — but to truly unlock their power, you need to know how to optimize them.]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Local large language models (LLMs) are becoming more capable and accessible than ever — but to truly unlock their power, you need to know how to optimize them.
Whether you&#8217;re running AI on an M-series MacBook or a 2019 Intel machine, there are tricks to get better speed, quality, and control.</p>
</div>
<div class="paragraph">
<p>In the final session of our GenAI series (after covering Local Dev, RAG, and Agents), we explored the real-world performance tuning of local LLMs.
Here&#8217;s what we covered 👇</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_running_llms_on_your_laptop">1. Running LLMs on Your Laptop</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_apple_silicon_vs_intel_macs">Apple Silicon vs Intel Macs</h3>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 42.8571%;">
<col style="width: 28.5714%;">
<col style="width: 28.5715%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Feature</th>
<th class="tableblock halign-left valign-top">Apple Silicon (M1/M2/M3/M4)</th>
<th class="tableblock halign-left valign-top">Intel Mac (e.g. 2019 MBP)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CPU &amp; GPU</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unified on one chip (SoC)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Separate chips for CPU and GPU</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Memory</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Shared Unified Memory Architecture</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">RAM for CPU, VRAM for GPU</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Model performance</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Fast, efficient, great for LLMs</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Slower, GPU mostly unused</p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>M-series chips shine for local AI workloads thanks to unified memory.</p>
</div>
<div class="paragraph">
<p>Intel Macs can still work well but rely entirely on CPUs and may struggle with larger models.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_what_can_you_run">What Can You Run?</h3>
<div class="paragraph">
<p>Models are measured by <strong>parameters</strong>, typically in billions (e.g., 3B, 7B, 13B).
More parameters = more "intelligence" — but also more memory use.</p>
</div>
<div class="paragraph">
<p>Use quantization (more below!) to fit larger models, even on machines with 8–16 GB RAM.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_quantization_making_llms_lighter">2. Quantization: Making LLMs Lighter</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_what_is_quantization">What is Quantization?</h3>
<div class="paragraph">
<p>Quantization reduces the <strong>precision of the numbers</strong> in a model — trading a little accuracy for huge gains in size and speed.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 40%;">
<col style="width: 40%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Format</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Typical Use</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">FP32</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Full precision</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Training</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">FP16</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Half precision</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Deployment</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">INT8</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Quantized</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Light inference (some loss)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">INT4</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Heavily quantized</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Fast local inference</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">INT2–3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Experimental</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Ultra-lightweight, niche</p></td>
</tr>
</tbody>
</table>
<div class="exampleblock">
<div class="content">
<div class="paragraph">
<p>LLaMA2-70B goes from <strong>138GB (FP16)</strong> to <strong>42GB (INT4)</strong> — a huge saving!</p>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_what_is_gguf">What is GGUF?</h3>
<div class="paragraph">
<p><code>GGUF</code> stands for <strong>GPT-Generated Unified Format</strong>.
It’s a modern, standardized file format designed for running <strong>quantized large language models (LLMs) locally</strong>.</p>
</div>
<div class="sect3">
<h4 id="_why_gguf">Why GGUF?</h4>
<div class="paragraph">
<p>GGUF makes loading and using models in tools like <code>llama.cpp</code>, <code>Ollama</code>, or <code>LM Studio</code>.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 75%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Feature</th>
<th class="tableblock halign-left valign-top">Benefit</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">🧳 Compact</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Stores quantized models in a smaller, efficient format</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">🧠 Self-contained</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Includes weights, tokenizer, metadata, and vocab in one file</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">⚡ Fast</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Optimized for quick loading and inference</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">🔧 Flexible</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Supports various quantization formats like <code>Q4_K_M</code>, <code>Q5_K_S</code>, etc.</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="_tools_that_support_gguf">Tools that Support GGUF</h4>
<div class="ulist">
<ul>
<li>
<p><code>llama.cpp</code> – Core backend for CPU/GPU inference</p>
</li>
<li>
<p><code>Ollama</code> – CLI + API for local models</p>
</li>
<li>
<p><code>LM Studio</code> – Desktop GUI for Mac/Windows/Linux</p>
</li>
<li>
<p><code>text-generation-webui</code> – Powerful browser-based frontend</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_example_gguf_model_filename">Example GGUF Model Filename</h4>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">mistral-7b-instruct-v0.1.Q4_K_M.gguf</code></pre>
</div>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 75%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Part</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mistral-7b-instruct</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Model type and size</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>v0.1</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Model version</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>Q4_K_M</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Quantization type (4-bit, medium group)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>.gguf</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">File format extension</p></td>
</tr>
</tbody>
</table>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Think of a <code>.gguf</code> file as an <strong>AI-in-a-box</strong> — it includes everything the model needs to run locally, compressed and ready to go.</p>
</div>
</blockquote>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_k_quant_types_for_gguf_models">K-Quant Types (for GGUF Models)</h3>
<div class="paragraph">
<p>When downloading quantized models (especially in GGUF format), you&#8217;ll often see suffixes like <code>Q4_K_S</code>, <code>Q5_K_M</code>, or <code>Q6_K_L</code>.
These aren&#8217;t just random labels — they define <strong>how the quantization is applied</strong>, and they directly affect <strong>model quality vs. performance</strong>.</p>
</div>
<div class="paragraph">
<p>The <code>K</code> in these names refers to the <strong>quantization group size and method</strong>, which influences:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>how much precision is preserved;</p>
</li>
<li>
<p>how fast the model can run;</p>
</li>
<li>
<p>how much RAM is required.</p>
</li>
</ul>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 75%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Suffix</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>K_S</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Small group quantization</strong>
Fastest and most memory-efficient. It compresses more aggressively, which means it&#8217;s ideal when you&#8217;re on a tight memory budget or running on weaker hardware — but output quality might noticeably degrade on complex tasks.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>K_M</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Medium group quantization</strong>
A great balance of speed, memory use, and output quality. It&#8217;s the "safe middle ground" for most users running 4–7B models locally. If you&#8217;re unsure where to start, this is a solid default.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>K_L</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>Large group quantization</strong>
More conservative compression — keeps more precision, giving higher-quality outputs. Slower and uses more RAM, but closer to non-quantized behavior. Ideal for tasks requiring accurate and nuanced responses.</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>The difference isn&#8217;t <strong>just</strong> in speed — it&#8217;s also in how much subtle detail the model retains in its responses.</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>“Think of <code>K_S</code>, <code>K_M</code>, and <code>K_L</code> as image compression presets:
<strong>Small</strong> is low-res JPEG, <strong>Medium</strong> is standard HD, <strong>Large</strong> is almost RAW quality.”</p>
</div>
</blockquote>
</div>
</div>
<div class="sect2">
<h3 id="_what_is_quantization_used_for">What is Quantization used for?</h3>
<div class="paragraph">
<p>Quantization is a trade-off between size, speed, and accuracy.
For some perspective, here’s a rough guide on how quantization is used in the LLM world:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 66.6667%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Task</th>
<th class="tableblock halign-left valign-top">Quantization</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Model training</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">FP32 (that&#8217;s why it is so expensive to train models, to get that knowledge accurate)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">For deployment</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Usually FP16 (for speed)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Local models</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unused INT4 (for speed and accuracy)</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_why_not_just_use_a_smaller_model">Why Not Just Use a Smaller Model?</h3>
<div class="ulist">
<ul>
<li>
<p>Models under 3B can run easily — but often lack reasoning or language nuance.</p>
</li>
<li>
<p>Quantization gives you <strong>the best of both worlds</strong>: keep a 7B+ model’s brain but shrink the size.</p>
</li>
</ul>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>“It’s like watching a 4K movie compressed to 1080p — smaller, still looks good.”</p>
</div>
</blockquote>
</div>
</div>
<div class="sect2">
<h3 id="_quantized_models_in_action_example">Quantized Models In Action (Example)</h3>
<div class="paragraph">
<p>In this example, we consider <a href="https://ollama.com/library/qwen2.5/tags">qwen2.5</a>, a 14B model with quantized versions available in <code>Ollama</code>.
We will focus on different quantization levels of the 14B model.</p>
</div>
<div class="paragraph">
<p>Let&#8217;s have a look at how different models deal with the following prompt:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">Explain recursion to a 10-year-old in one paragraph.</code></pre>
</div>
</div>
<div class="paragraph">
<p>To run the models, execute one of the following prompts, starting with Q4_0:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-shell" data-lang="shell">ollama run qwen2.5:14b-instruct-q4_0
ollama run qwen2.5:14b-instruct-q8_0
ollama run qwen2.5:14b-instruct-q2_K
ollama run qwen2.5:14b-instruct-fp16</code></pre>
</div>
</div>
<div class="paragraph">
<p>For example, let&#8217;s start with Q4_0:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-shell" data-lang="shell">ollama run qwen2.5:14b-instruct-q4_0</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now that the model is loaded, we can run the prompt:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-shell" data-lang="shell">"Explain recursion to a 10-year-old in one paragraph."</code></pre>
</div>
</div>
<div class="paragraph">
<p>Pay attention to the response time and the memory usage.
Compare it with the other models.</p>
</div>
<div class="paragraph">
<p>You may have noticed there is not much difference in quality between the Q4_0 and Q8_0 models, but the Q2_K model is much faster and smaller.
Perfect for showing the quality/speed trade-off in action, and how to adjust for your needs.
This does not necessarily mean that the behavior is the same for other prompts or tasks.
You have to try this for yourself and see what works best for you on your machine.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_3_tuning_parameters_in_ollama">3. Tuning Parameters in Ollama</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Using <strong>Ollama</strong>?
You can change some parameter settings of the local models based on your preference, for example, a more deterministic response, or a more creative one.</p>
</div>
<div class="paragraph">
<p>Let us consider the <code>llama3</code> model, we can run it with the following command:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-shell" data-lang="shell">ollama run llama3</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can set these parameters after the model is loaded:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>/set parameter &lt;parameter&gt; &lt;value&gt;</pre>
</div>
</div>
<div class="paragraph">
<p>So, for example, to set <code>temperature</code> to 1.0:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>/set parameter temperature 1.0</pre>
</div>
</div>
<div class="paragraph">
<p>And then we set <code>top_p</code> to 0.9:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>/set parameter top_p 0.9</pre>
</div>
</div>
<div class="paragraph">
<p>In the example above, we set the <code>temperature</code> to 1.0, a more creative response, and <code>top_p</code> to 0.9, a more deterministic response.
Parameter <code>temperature</code> adds randomness.
The lower the value, the more focused and deterministic the model response.
The higher the value, the more creative and varied the response.
Parameter <code>top_p</code> picks from the smallest possible set of words whose cumulative probability adds up to <code>p</code>.
It controls diversity — higher values mean more diverse and creative responses, and lower values make responses more focused.</p>
</div>
<div class="paragraph">
<p>Here are some more common parameters you can tune:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 50%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Param</th>
<th class="tableblock halign-left valign-top">What it Does</th>
<th class="tableblock halign-left valign-top">Typical Values</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">num_ctx</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Context size (how much it remembers)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2048–4096</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">top_k</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Limits top options for output</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">40–100</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">top_p</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Controls diversity</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0.8–0.95</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">temperature</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Controls creativity</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">0.6–0.8 (chat), 0.3–0.6 (code)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">repeat_penalty</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Avoids repeating phrases</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1.1–1.3</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">threads</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of CPU threads (config only)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Match to physical cores</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>If you want to learn more about the parameters, you can find some extra information <a href="https://learnprompting.org/blog/llm-parameters?srsltid=AfmBOoorA2XSH8rxtzvLZcSstK1mp8Hzrj-o5uJRIXKOHVUhAmvcsW5u">here</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_resources">Resources</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://www.tensorops.ai/post/what-are-quantized-llms">What Are Quantized LLMs – TensorOps</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=K75j8MkwgJ0">Quantization Explained – YouTube</a></p>
</li>
<li>
<p><a href="https://pieces.app/blog/llm-parameters">LLM Parameters – Pieces Blog</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_final_thoughts">Final Thoughts</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Running LLMs locally is no longer science fiction — it&#8217;s practical, efficient, and private.
With just a bit of tuning and the right model format, your laptop becomes an AI powerhouse.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Part 1: Introduction and Stack Breakdown for the Angular + NestJS Auth Boilerplate</title>
		<link>https://blog.lunatech.com//posts/2025-04-04-part-1-introduction-&amp;-stack-breakdown-for-the-angular-+-nestjs-auth-boilerplate</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-04-04T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[angular]]></category>
                }
             {
            <category><![CDATA[nestjs]]></category>
                }
             {
            <category><![CDATA[postgresql]]></category>
                }
             {
            <category><![CDATA[typeorm]]></category>
                }
             {
            <category><![CDATA[jwt]]></category>
                }
             {
            <category><![CDATA[authentication]]></category>
                }
             {
            <category><![CDATA[frontend]]></category>
                }
             {
            <category><![CDATA[typescript]]></category>
                }
             {
            <category><![CDATA[nodejs]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-04-04-part-1-introduction-&amp;-stack-breakdown-for-the-angular-+-nestjs-auth-boilerplate</guid>

					<description>
                        <![CDATA[ Welcome to the first part in this series on building a full-stack]]></description>
                    <content:encoded><![CDATA[
                    <div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Welcome to the first part in this series on building a full-stack
authentication boilerplate with <strong>Angular</strong>, <strong>NestJS</strong>, and <strong>PostgreSQL</strong>.</p>
</div>
<div class="paragraph">
<p>If you’re a backend developer comfortable with APIs, databases, and business
logic—but frontend frameworks feel like foreign territory—this series is for
you.
We’ll walk through the full stack, showing you how to build real
authentication flows while picking up modern Angular features along the way.</p>
</div>
<div class="paragraph">
<p>By the end, you’ll have a production-ready authentication boilerplate you
can reuse in your own projects—or hand off confidently to frontend
collaborators.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_why_angular_nestjs">Why Angular + NestJS?</h3>
<div class="paragraph">
<p>We’re using <strong>Angular</strong> on the frontend and <strong>NestJS</strong> on the backend.
Both are built in <strong>TypeScript</strong>, so you get a unified development experience
without switching mental models.</p>
</div>
<div class="paragraph">
<p>Here’s why this stack works well together:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Angular</strong> is opinionated, scalable, and modular—perfect for building real
apps, not just prototypes.
Angular 19 introduces useful features like Signals and new control flow
syntax that cut boilerplate and improve performance.</p>
</li>
<li>
<p><strong>NestJS</strong> is a structured backend framework with decorators, guards, and
strong support for things like JWTs and PostgreSQL—ideal for secure
authentication systems.</p>
</li>
<li>
<p><strong>PostgreSQL</strong> is a reliable, battle-tested relational database with great
tooling and support for user/session management.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Together, this stack gives you a clean, testable, and scalable
foundation—without gluing together a bunch of libraries yourself.</p>
</div>
</div>
<div class="sect2">
<h3 id="_what_youll_build">What You’ll Build</h3>
<div class="paragraph">
<p>Over this series, you’ll create a modular, full-stack authentication
boilerplate with:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>A NestJS backend featuring JWT login and registration</p>
</li>
<li>
<p>A styled Angular frontend with auth forms and route guards</p>
</li>
<li>
<p>Token management, global error handling, and frontend state control</p>
</li>
<li>
<p>CI/CD pipelines, testing setup, and a containerized dev environment</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>This isn’t a toy app—it’s a starting point for real projects.</p>
</div>
</div>
<div class="sect2">
<h3 id="_whats_new_in_angular_19">What’s New in Angular 19</h3>
<div class="paragraph">
<p>Angular 19 introduces major improvements that simplify frontend development
and reduce complexity:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Signals</strong> – A new reactive state primitive.
Think of it like <code>useState</code>, but built into Angular.
Great for managing form state and authentication status.</p>
</li>
<li>
<p><strong>Control Flow Syntax</strong> – <code>@if</code>, <code>@for</code>, and <code>@switch</code> clean up your
templates and reduce the need for verbose structural directives.</p>
</li>
<li>
<p><strong>Defer Blocks</strong> – Lazily load components based on app state, perfect for
gating authenticated vs. public views.</p>
</li>
<li>
<p><strong>Improved Change Detection</strong> – Smarter re-rendering out of the box for
better performance.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>We’ll use these features where they make sense—especially in the login and
registration flows—so you learn by doing.</p>
</div>
</div>
<div class="sect2">
<h3 id="_who_this_is_for">Who This Is For</h3>
<div class="paragraph">
<p>This series is for backend developers who want to:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Understand modern Angular concepts without getting lost in frontend jargon</p>
</li>
<li>
<p>Build secure, token-based authentication flows using best practices</p>
</li>
<li>
<p>Deploy real full-stack apps with confidence</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You don’t need Angular experience.
If you know TypeScript, you’ll feel at home.</p>
</div>
</div>
<div class="sect2">
<h3 id="_tools_youll_need">Tools You’ll Need</h3>
<div class="sect3">
<h4 id="_required">Required</h4>
<div class="ulist">
<ul>
<li>
<p><strong>Node.js</strong> – Backend + Angular CLI</p>
</li>
<li>
<p><strong>NestJS CLI</strong> – Backend scaffolding</p>
</li>
<li>
<p><strong>Angular CLI</strong> – Frontend scaffolding</p>
</li>
<li>
<p><strong>PostgreSQL</strong> – Local dev database</p>
</li>
<li>
<p><strong>Git</strong> – Version control</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="_optional_but_helpful">Optional (but helpful)</h4>
<div class="ulist">
<ul>
<li>
<p><strong>Postman</strong> – For testing APIs</p>
</li>
<li>
<p><strong>pgAdmin</strong> – For inspecting the database</p>
</li>
<li>
<p><strong>VS Code</strong> – With TypeScript extensions</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_getting_set_up">Getting Set Up</h3>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Install Node.js</strong><br>
<a href="https://nodejs.org" class="bare">https://nodejs.org</a></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">node -v
npm -v</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Install Angular and NestJS CLIs</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">npm install -g @angular/cli @nestjs/cli</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Install PostgreSQL</strong><br>
<a href="https://www.postgresql.org/download" class="bare">https://www.postgresql.org/download</a></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-sql" data-lang="sql">CREATE DATABASE auth_boilerplate;</code></pre>
</div>
</div>
</li>
<li>
<p><strong>Initialize Git Repo</strong></p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-bash" data-lang="bash">git init</code></pre>
</div>
</div>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_coming_up_next_part_2">Coming Up Next (Part 2)</h3>
<div class="paragraph">
<p>In Part 2, we’ll scaffold the NestJS backend, hook it up to PostgreSQL with
TypeORM, and implement JWT-based login and registration logic you can
actually ship.</p>
</div>
</div>
<div class="sect2">
<h3 id="_final_thoughts">Final Thoughts</h3>
<div class="paragraph">
<p>This series is about building something real—not another tutorial app you
toss out after reading.
Whether it’s for a SaaS product, side project, or production tool, having a
solid authentication foundation makes every other feature easier to build.</p>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Full-Stack Authentication Boilerplate: Angular + NestJS + PostgreSQL</title>
		<link>https://blog.lunatech.com//posts/2025-04-04-full-stack-authentication-boilerplate:-angular-+-nestjs-+-postgresql</link>
		
		<dc:creator><![CDATA[Jake Ortega]]></dc:creator>
        <pubDate>2025-04-04T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[angular]]></category>
                }
             {
            <category><![CDATA[nestjs]]></category>
                }
             {
            <category><![CDATA[postgresql]]></category>
                }
             {
            <category><![CDATA[typeorm]]></category>
                }
             {
            <category><![CDATA[jwt]]></category>
                }
             {
            <category><![CDATA[authentication]]></category>
                }
             {
            <category><![CDATA[frontend]]></category>
                }
             {
            <category><![CDATA[typescript]]></category>
                }
             {
            <category><![CDATA[nodejs]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-04-04-full-stack-authentication-boilerplate:-angular-+-nestjs-+-postgresql</guid>

					<description>
                        <![CDATA[ For backend developers who want to ship full-stack projects—without wrestling with frontend complexity.]]></description>
                    <content:encoded><![CDATA[
                    <h1 id="_a_practical_series_for_backend_developers_building_secure_full_stack_apps" class="sect0">A practical series for backend developers building secure full-stack apps</h1>
<div class="paragraph">
<p>For backend developers who want to ship full-stack projects—without wrestling with frontend complexity.</p>
</div>
<div class="paragraph">
<p>We’re building a <strong>full-stack authentication boilerplate</strong>—a reusable, modular codebase that gives you login, registration, route protection, token handling, and database integration out of the box. Clone it, customize it, and start building faster.</p>
</div>
<div class="sect2">
<h3 id="_why_this_stack">Why This Stack?</h3>
<div class="paragraph">
<p>We chose <strong>Angular 19</strong> and <strong>NestJS</strong> for a reason:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>TypeScript end-to-end</strong>: Write frontend and backend in the same language with consistent tooling.</p>
</li>
<li>
<p><strong>Angular 19</strong> introduces Signals and control flow syntax (<code>@if</code>, <code>@for</code>), simplifying state and UI logic.</p>
</li>
<li>
<p><strong>NestJS</strong> is structured, modular, and test-friendly—ideal for developers coming from Spring or .NET.</p>
</li>
<li>
<p><strong>PostgreSQL</strong> + <strong>TypeORM</strong> provide a robust relational layer with strong typing and migrations.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You <strong>could</strong> build this with React, Express, or MongoDB. But we picked this stack for its structure and scalability—great for teams or solo developers who want a solid foundation, not just flexibility.</p>
</div>
</div>
<div class="sect2">
<h3 id="_whats_in_the_series">What’s in the Series?</h3>
<div class="paragraph">
<p>Here’s the roadmap 👇</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../media/2025-04-04-full-stack-authentication-boilerplate:-angular-+-nestjs-+-postgresql/roadmap.png" alt="Roadmap overview">
</div>
</div>
<div class="paragraph">
<p>Each post builds on the last, walking you through:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>✅ JWT login &amp; registration</p>
</li>
<li>
<p>✅ Angular route guards &amp; auth state</p>
</li>
<li>
<p>✅ NestJS modules, guards, and services</p>
</li>
<li>
<p>✅ PostgreSQL setup with TypeORM</p>
</li>
<li>
<p>✅ CI/CD, E2E tests with Playwright, and Docker deployment</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You don’t need prior Angular or NestJS experience—just TypeScript and backend fundamentals.</p>
</div>
</div>
<div class="sect2">
<h3 id="_why_this_series_exists">Why This Series Exists</h3>
<div class="paragraph">
<p>You don’t need another to-do app.</p>
</div>
<div class="paragraph">
<p>You need a <strong>working authentication foundation</strong>—something you can reuse, extend, and deploy. That’s what this series delivers, while helping you build real frontend + backend skills along the way.</p>
</div>
<div class="paragraph">
<p>Let’s build something you can actually ship.</p>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Mastering Typeclass Derivation with Scala 3</title>
		<link>https://blog.lunatech.com//posts/2025-03-07-typeclass-derivation</link>
		
		<dc:creator><![CDATA[Gustavo De Micheli]]></dc:creator>
        <pubDate>2025-03-07T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[scala]]></category>
                }
             {
            <category><![CDATA[typeclass]]></category>
                }
             {
            <category><![CDATA[metaprogramming]]></category>
                }
             {
            <category><![CDATA[we-know-scala]]></category>
                }
             {
            <category><![CDATA[scala-lujah]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-03-07-typeclass-derivation</guid>

					<description>
                        <![CDATA[ When learning about programming we stumble upon the concept of Polymorphism, and]]></description>
                    <content:encoded><![CDATA[
                    <div class="sect1">
<h2 id="_polymorphism">Polymorphism</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When learning about programming we stumble upon the concept of Polymorphism, and
when learning something new I always wonder, why should we care? Besides getting
a new tool on our tool belt I think it&#8217;s important to understand what are we
getting from this new programming trick.</p>
</div>
<div class="paragraph">
<p>Polymorphism, from the Greek "having multiple forms", allows us to define and
implement a set of different algorithms using the same interface. This has the
benefit of lowering the complexity of our system from our client&#8217;s point of view.
By switching to another implementation, they don&#8217;t need to understand what changed
underneath, they just need to know that the interface is honored by another
implementation. And by lowering this complexity we lower the cognitive-load developers
need to have while using our API, making their code easier to maintain.</p>
</div>
<div class="paragraph">
<p>As far as I&#8217;m aware, there are at least 3 types of polymorphism:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Ad-Hoc</p>
</li>
<li>
<p>Parametric, and</p>
</li>
<li>
<p>Sub-type</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If you come from a Object Oriented programming background you&#8217;ll recognize the last
one. Sub-type polymorphism is implemented by defining a parent class that
defines an API, and child classes implementing it. Then using a mechanism like
double-dispatch, a implementation is chosen based on the actual subclass being
instantiated:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">trait Shape:
    def area: Double

class Square(width: Double) extends Shape:
    override def area: Double = width * width

class Rectangle(width: Double, height: Double) extends Shape:
    override def area: Double = width * height

class Circle(radius: Double) extends Shape:
    override def area: Double = Math.pow(radius, 2) * Math.PI

val s1: Shape = new Circle(4)
val s2: Shape = new Rectangle(4, 2)

s1.area
s2.area</code></pre>
</div>
</div>
<div class="paragraph">
<p>Another type of polymorphism we can find in both Functional and Object Oriented
programming languages is Parametric polymorphism where we don&#8217;t necessarily
provide different implementations based on different types, but rather abstract
over some types:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">def identity[A](a: A): A = a

enum List[+A]:
    case Cons(head: A, tail: List[A]) extends List[A]
    case Nil extends List[Nothing]

    def map[B](fn: A =&gt; B): List[B] = this match
        case Cons(head, tail) =&gt; Cons(fn(head), tail.map(fn))
        case Nil =&gt; Nil</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>List</code> doesn&#8217;t care what&#8217;s containing, but its <code>map</code> method can easily work
for `Int`s, `String`s, and so on.</p>
</div>
<div class="paragraph">
<p>The last type of polymorphism we mentioned was <em>Ad-Hoc</em> polymorphism, which can
be defined as a "A system where functions or expressions are defined for specific
types". Let&#8217;s see how this gets implemented using Typeclasses.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_typeclasses">Typeclasses</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Typeclasses are a way to implement Ad-Hoc polymorphism with 2 significant properties:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Separate Definitions: For each relevant class, we define a separate instance of
a function or method (like <code>Show</code>, <code>Eq</code>, etc.) This similar to defining a
separate function for specific types.</p>
</li>
<li>
<p>Context-Dependent Selection: An implementation will be selected based on where
the typeclass is used.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In Scala, a Typeclass is implemented by defining 3 parts:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Interface: What functionality is the Typeclass offering.</p>
</li>
<li>
<p>Instances: How is that functionality implemented.</p>
</li>
<li>
<p>Usage: Where and how is that functionality used.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Let&#8217;s see a simple implementation of the <code>Show</code> Typeclass:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">// Interface
trait Show[A]:
    def show(a: A): String

// Instances
object Show:
    given Show[Int]     = (i: Int) =&gt; i.toString
    given Show[String]  = (s: String) =&gt; "'" + s + "'"
    given Show[Boolean] = (b: Boolean) =&gt; i.toString

// Usage
def log[A](a: A)(using s: Show[A]): Unit =
    println(s.show(a))</code></pre>
</div>
</div>
<div class="paragraph">
<p>An important detail of the Typeclass definition is that we use a Type Parameter
on it&#8217;s interface, instances, and usage.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_typeclass_derivation">Typeclass Derivation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>An interesting feature of Scala is that we can automatically generate the
instances of a Typeclass by implementing Typeclass Derivation code. This will
make the compiler generate instances at compile-time, making our Typeclass more
usable as we&#8217;ll shift the burden of implementation to our code instead of our
client&#8217;s code.</p>
</div>
<div class="paragraph">
<p>To derive instances for a Typeclass we need:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Tools to decompose complex types, such as case classes.</p>
</li>
<li>
<p>Tools to compose complex types from more simpler types.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>And one way we can approach implementing Derivation code is:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Think Recursively (Base vs. Iterative Case)</p>
</li>
<li>
<p>Implementing one Typeclass by implementing a simpler version first.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>On Scala 2 we&#8217;d use a library like <a href="https://github.com/milessabin/shapeless">Shapeless</a> or
<a href="https://github.com/softwaremill/magnolia">Magnolia</a> to compose and decompose complex
types. In Scala 3 these features are backed in the language.</p>
</div>
<div class="paragraph">
<p><a href="https://docs.scala-lang.org/scala3/reference/contextual/derivation.html#mirror-1">Mirrors</a> provide
typelevel information about types being derived, with similar features as <em>HList</em> and
<em>Coproduct</em> in a single abstraction.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">import scala.collection.AbstractIterable
import scala.compiletime.{erasedValue, error, summonInline}
import scala.deriving.*

// Interface
trait Show[A]:
    def show(a: A): String

// Instances
object Show:
    given Show[Int]     = (i: Int) =&gt; i.toString
    given Show[String]  = (s: String) =&gt; "'" + s + "'"
    given Show[Boolean] = (b: Boolean) =&gt; i.toString

    def iterable[T](p: T): Iterable[Any] = new AbstractIterable[Any]:
        def iterator: Iterator[Any] = p.asInstanceOf[Product].productIterator

    def showProduct[T](p: Mirror.ProductOf[T], elems: =&gt; List[Show[?]]): Show[T] =
        new Show[T]:
            def show(a: A): String =
                iterable(x).lazyZip(elems).map { case (i, s) =&gt; s.show(i) }.mkString(",")

    inline def derived[A](using m: Mirror.ProductOf[A]): Show[A] =
        lazy val elemInstances = summonInstances[T, m.MirroredElemTypes]
        showProduct(m, elemInstances)

    inline def summonInstances[T, Elems &lt;: Tuple]: List[Show[?]] =
        inline erasedValue[Elems] match
            case _: (elem *: elems) =&gt; deriveOrSummon[T, elem] :: summonInstances[T, elems]
            case _: EmptyTuple =&gt; Nil

    inline def deriveOrSummon[T, Elem]: Show[Elem] =
        inline erasedValue[Elem] match
            case _: T =&gt; deriveRec[T, Elem]
            case _    =&gt; summonInline[Show[Elem]]

    inline def deriveRec[T, Elem]: Show[Elem] =
        inline erasedValue[T] match
            case _: Elem =&gt; error("infinite recursive derivation")
            case _       =&gt; Show.derived[Elem](using summonInline[Mirror.Of[Elem]]) // recursive derivation</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is a simplified example of a Typeclass Derivation for the <code>Show</code> Typeclass.
For a more thorough example with a more detailed explanation, I cannot recommend
<a href="https://docs.scala-lang.org/scala3/reference/contextual/derivation.html">Type Class Derivation</a> enough.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>The Scala effect: Java’s Evolution Inspired by Scala</title>
		<link>https://blog.lunatech.com//posts/2025-02-28-the-scala-effect</link>
		
		<dc:creator><![CDATA[]]></dc:creator>
        <pubDate>2025-03-04T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[jvm]]></category>
                }
             {
            <category><![CDATA[scala]]></category>
                }
             {
            <category><![CDATA[java]]></category>
                }
             {
            <category><![CDATA[java8]]></category>
                }
             {
            <category><![CDATA[java11]]></category>
                }
             {
            <category><![CDATA[java17]]></category>
                }
             {
            <category><![CDATA[java21]]></category>
                }
             {
            <category><![CDATA[we-know-scala]]></category>
                }
             {
            <category><![CDATA[scala-lujah]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-02-28-the-scala-effect</guid>

					<description>
                        <![CDATA[ Since its inception, Java has been one of the most widely used programming languages,]]></description>
                    <content:encoded><![CDATA[
                    <div class="sect1">
<h2 id="_contents">Contents</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Since its inception, Java has been one of the most widely used programming languages,
known for its stability and enterprise adoption.
However, as modern software development demands more expressive and concise code,
Java has often looked to other languages for inspiration.
One of its biggest influences has been Scala—a language that brought functional programming,
immutability, and expressive syntax to the JVM.</p>
</div>
<div class="paragraph">
<p>Over the years, Java has steadily incorporated many features that were first introduced in Scala,
from lambda expressions and pattern matching to records and data-oriented programming.
In this article, we’ll explore how Java has evolved by adopting concepts from Scala,
breaking down the changes version by version. We&#8217;ll also see some examples,
to see each language&#8217;s unique approach, while pointing out some key differences.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_8">Java 8</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_stream_api">Stream API</h3>
<div class="paragraph">
<p>I don&#8217;t think I need to introduce many of the readers to the Stream API, born in Java 8.
It is also well known, how it basically changed the game on how you approach operations on collections.
What might be a bit less known is the fact, that this feature is a big step towards functional programming,
since the API promotes functional paradigms, such as immutability, pure functions, higher level functions.
Exploring those paradigms could use a talk in itself, so the below example is rather to show,
from a developer experience perspective how easy it is to do multiple, small operations on a collection,
with the new API compered to the old way.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">def getNamesOfMammals(animals: List[Animal]): List[String] =
  animals
    .filter(_.classification == "mammal")
    .map(_.name)</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">private List&lt;String&gt; getNamesOfMammals(List&lt;Animal&gt; animals) {
    List&lt;String&gt; names = new ArrayList&lt;&gt;();

    for (Animal animal: animals) {
        if (animal.classification.equals("mammal")) {
            names.add(animal.name);
        }
    }

    return names;
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">private List&lt;String&gt; getNamesOfMammals(List&lt;Animal&gt; animals) {
    return animals
        .stream()
        .filter(animal -&gt; animal.classification.equals("mammal"))
        .map(animal -&gt; animal.name)
        .toList();
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="paragraph">
<p>It is worth to note, that the above example is nice, and already a huge step in the right direction,
when it comes to streaming data processing, java still lacks support for a lot of major operations (fold, sliding windows).</p>
</div>
</div>
<div class="sect2">
<h3 id="_optional">Optional</h3>
<div class="paragraph">
<p>Safety first! Especially when it comes to nulls, and java. <code>Option</code> is a core part of the Scala language,
making nullable data a breeze to work with, through great support for operations, and branchless behaviour definition.
Java intends to implement some of that functionality with the <code>Optional</code> class. Just like in the streams' case,
scala still offers more flexibility and more defined developer options, when it comes to potentially missing data (e.g.: <code>exists</code>, <code>forall</code>).
The example illustrates the boilerplate needed to handle nested nullable structures,
and the positive steps java already took in the right direction.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">case class Wing(length: Int, fact: Option[String])
case class Animal(name: String, wing: Option[Wing])

def printSafeInterestingWingFacts(animals: List[Animal]) =
  animals.foreach { animal =&gt; println(s"""${animal.name}: ${
    animal.wing
      .flatMap(_.fact)
      .getOrElse("no interesting wing fact :(")}""")}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">class Wing{public Integer length; public String fact;}
class Animal{public String name; public Wing wing;}

private void printInterestingWingFacts(List&lt;Animal&gt; animals) {
    for (Animal animal: animals) {
        if (animal.wing != null) {
            if (animal.wing.fact != null) {
                System.out.printf("%s: %s%n", animal.name, animal.wing.fact);
            } else {
                System.out.printf("%s: no interesting wing fact :(%n", animal.name);
            }
        } else {
            System.out.printf("%s: no interesting wing fact :(%n", animal.name);
        }
    }
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">class Wing{public Integer length; public Optional&lt;String&gt; fact;}
class Animal{public String name; public Optional&lt;Wing&gt; wing;}

private void printSafeInterestingWingFacts(List&lt;Animal&gt; animals) {
    animals.forEach(animal -&gt; {
        System.out.printf("%s: %s%n", animal.name, animal.wing
            .flatMap(wing -&gt; wing.fact)
            .orElse("no interesting wing fact :(%n"));
    });
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_functional_interfaces_lambdas">Functional interfaces (lambdas)</h3>
<div class="paragraph">
<p>One of the key prerequisites of having support for all these functional paradigms, is to be able to pass functions as arguments.
Java implements this concept via so-called functional interfaces. A functional interface, is an interface,
with only one method declared. Java 8 also makes it easier to use these interfaces with the lambda syntax.
With the lambda syntax you can pass lambda functions as arguments, and the compiler will be able to interpret it,
as the needed interface.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">def chainEvenNumbers(numbers: List[Int]): String =
  numbers
  .filter(n =&gt; n % 2 == 0)
  .map(n =&gt; n.toString)
  .reduce{case (n1, n2) =&gt; n1 + n2}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">String chainEvenNumbers(List&lt;Integer&gt; numbers) {
    Predicate&lt;Integer&gt; predicate = new Predicate&lt;&gt;() {
        @Override
        public boolean test(Integer integer) {
            return integer % 2 == 0;
        }
    };

    Function&lt;Integer, String&gt; mapper = new Function&lt;&gt;() {
        @Override
        public String apply(Integer integer) {
            return integer.toString();
        }
    };

    BinaryOperator&lt;String&gt; accumulator = new BinaryOperator&lt;&gt;() {
        @Override
        public String apply(String s, String s2) {
            return s + s2;
        }
    };

    return numbers
        .stream()
        .filter(predicate)
        .map(mapper)
        .reduce("", accumulator);
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">String chainEvenNumbers(List&lt;Integer&gt; numbers) {
    return numbers
        .stream()
        .filter(i -&gt; i % 2 == 0)
        .map(Object::toString)
        .reduce("", (s, s2) -&gt; s + s2);
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_11">Java 11</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Java 11&#8217;s main features fall outside of this article&#8217;s scope, since they are not directly influenced by Scala,
nor resemble existing Scala features. I would like to mention however a couple small additions,
mainly in the area of scripting, and writing / running fast, without overhead.
OVer the years a good argument for learning Scala (or against learning java, really) was that for a beginner,
the learning curve might be steep, and writing small code snippets is not really possible.
On top of that, while it scales up excellent, it is not really ideal for small projects.
The following additions you can argue how much they were inspired by Scala actually (I wouldn&#8217;t argue against it either),
but one thing is not arguable: Scala was always ahead in these topics, and with 11, Java is now one step closer.</p>
</div>
<div class="sect2">
<h3 id="_collectionof_factory_method_java_9"><code><em>&lt;Collection&gt;</em>::of</code> factory method (Java 9)</h3>
<div class="paragraph">
<p>No more dubious initialization, and element adding after, with Java 11 now most of the commonly used collections receive
the <code>::of</code> factory method, where you can quickly create a new immutable instance, with a number of initial elements.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">val l: List[Int] = List(1, 2, 3, 4, 5)</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">List&lt;Integer&gt; l = new ArrayList&lt;&gt;();

l.add(1);
l.add(2);
l.add(3);
l.add(4);
l.add(5);</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">List&lt;Integer&gt; l = List.of(1, 2, 3, 4, 5);</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_introduction_of_jshell_java_repl_java_9">Introduction of <code><em>jshell</em></code> (java REPL) (Java 9)</h3>
<div class="paragraph">
<p>Huge step towards scripting, but also really important for bigger projects. The presence of the jshell
is equally amazing for absolute beginners, as well as seasoned developers. It could be used in various ways,
from writing quick <em>"hello world"</em> code to quickly test out more serious code in a bigger production environment.
The java REPL is not yet that advanced as the Scala counterpart, but this long overdue feature
is again making the language a bit more consumable.</p>
</div>
</div>
<div class="sect2">
<h3 id="_addition_of_var_keyword_java_11">Addition of var keyword (Java 11)</h3>
<div class="paragraph">
<p>If it was not a 100% clear what does it mean, when we talk about steep learning curve for beginners,
or challenges to test out code segment quickly in a bigger environment,
having to quickly write a code like this (in both cases) hopefully explains it:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">void testStateAndTranslator() {
    InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState state = nimbus.getState();
    assert state == expectedState;
    RefreshAuthorizationPolicyProtocolServerSideTranslatorPB translator = hadoop.getTranslator();
    assert translator == expectedTranslator;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now this is a test, where you would most likely get some autocorrection, but imagine the nightmare typing these in the REPL!
Luckily if you want to quicly test out something like this, sice java 11 it would only look like something like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">jshell&gt; var state = nimbus.getState()
state ==&gt; InternalFrameInternalFrameTitlePaneInternalFrameT ... owNotFocusedState@1a86f2f1

jshell&gt; state.testStuff()
Stuff works!</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_launch_single_file_java_11">Launch single file (Java 11)</h3>
<div class="paragraph">
<p>This is all great so far, but what if you want to write some code quickly to generate input, or want to process a file?
You wouldn&#8217;t want to include that file in your project, and definitely don&#8217;t want to recompile every time you tweak that
<code>println</code> statement. With Java 11, you get another benefit which points java in the scripting direction.
You can run single java files, without associating it to a project, or even without precompiling it.
In fact you can include a <code>she-bang</code> line, and run it as a shell script!</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">❯ javac HelloWorldJava8.java
❯ java HelloWorldJava8
Hello World!
❯ java HelloWorldJava11.java
Hello World!
❯ ./HelloWorldJava11Shell.java
Hello World!</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_17">Java 17</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_switch_expressions_java_14">Switch expressions (Java 14)</h3>
<div class="paragraph">
<p>Switch expressions' functionality is still limited to the same restrictions as before
(we are going to talk about these restrictions later), but it got a nice facelift,
where you can leave a lot of boilerplate code behind!</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">def kindergartenToString(number: Int): String =
  number match {
  case 1 =&gt; "1"
  case 2 =&gt; "2"
  case _ =&gt; "cannot count until that"
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">private String kindergartenToString(Integer number) {
    String result;
    switch (number) {
        case 1:
            result = "1";
            break;
        case 2:
            result = "2";
            break;
        default:
            result = "cannot count until that";
    };

    return result;
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">private String kindergartenToString(Integer number) {
    return switch (number) {
        case 1 -&gt; "1";
        case 2 -&gt; "2";
        default -&gt; "cannot count until that";
    };
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_text_blocks_java_15">Text blocks (Java 15)</h3>
<div class="paragraph">
<p>Another long overdue feature, where you wonder, why exactly did we need to wait until Java 15 for that?</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">var sql =
  """
    |SELECT
    |  name,
    |  salary,
    |  title
    |FROM
    |  employees
    |WHERE
    |  age &lt; 25
    |  AND title in (
    |    SELECT
    |      summary
    |    FROM
    |      jobs
    |  )
    |""".stripMarging</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">String sql = "SELECT\n" +
            "  name,\n" +
            "  salary,\n" +
            "  title\n" +
            "FROM\n" +
            "  employees\n" +
            "WHERE\n" +
            "  age &lt; 25\n" +
            "  AND title in (\n" +
            "    SELECT\n" +
            "      summary\n" +
            "    FROM\n" +
            "      jobs\n" +
            "  )";</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">String sql = """
            SELECT
              name,
              salary,
              title
            FROM
              employees
            WHERE
              age &lt; 25
              AND title in (
                SELECT
                  summary
                FROM
                  jobs
              )""";</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_pattern_matching_instanceof_java_16">Pattern matching instanceof (Java 16)</h3>
<div class="paragraph">
<p>You can argue if this in itself is a huge step or not, but this definitely set the table, for what&#8217;s to come with Java 21.
And not having to cast (although it is 100% safe, already from context) by hand every time,
and having to have either multiple casts, or nested branches is already a big step, but let the code speak for itself.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">def callIfPositiveInt(any: Any): Unit =
  any match {
    case i: Int =&gt; i.someMethodOnInt()
  }</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">void callIfPositiveIntImpl1(Object object) {
    if (object instanceof Integer) {
        Integer i = (Integer) object;

        if (i &gt; 0) {
            i.someMethodOnInteger();
        }
    }
}

void callIfPositiveIntImpl2(Object object) {
    if (object instanceof Integer &amp;&amp; ((Integer) object) &gt; 0) {
        Integer i = (Integer) object;
        i.someMethodOnInteger();
    }
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">void callIfPositiveInt(Object object) {
    if (object instanceof Integer i &amp;&amp; i &gt; 0) {
        i.someMethodOnInteger();
    }
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_record_classes_java_16">Record classes (Java 16)</h3>
<div class="paragraph">
<p>Record classes are a new type declaration, that allows you to compactly define immutable data classes.
They are intended to be transparent carriers of their shallowly immutable data.</p>
</div>
<div class="paragraph">
<p>They resemble the well known <code>case class</code> in Scala. There&#8217;s no such thing as "same" between two languages,
so it goes without saying that they have (on top of the many similarities) some differences.
Exactly discovering all the similarities and differences is outside of the scope of this article,
but from a developer experience perspective, being able to write compact code that speaks for itself
is the same for both.</p>
</div>
<div class="paragraph">
<p>Records are going to be even more powerful in Java 21, paired with pattern matching concepts.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">case class Point(x: Int, y: Int)


val point = Point(1, 2)
val x = point.x
val y = point.y
val hashCode = point.hashCode
val s = point.toString // "Point[x=1, y=2]"

assert(point == Point(1, 2))
assert(!(point == Point(2, 2)))</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">class Point {
    private final Integer x;
    private final Integer y;

    public Point(Integer x, Integer y) {
        this.x = x;
        this.y = y;
    }

    public Integer x() {
        return x;
    }

    public Integer y() {
        return y;
    }

    @Override
    public String toString() {
        return String.format("Point[x=%d, y=%d]", x, y);
    }

    @Override
    public boolean equals(Object object) {
        if (this == object) return true;
        if (!(object instanceof Point point)) return false;
        return Objects.equals(x, point.x) &amp;&amp; Objects.equals(y, point.y);
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}


Point point = new Point(1, 2);
Integer x = point.x();
Integer y = point.y();
int hashCode = point.hashCode();
String s = point.toString(); // "Point[x=1, y=2]"
assert point.equals(new Point(1, 2));
assert !point.equals(new Point(2, 2));</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">record Point(Integer x, Integer y) {}


Point point = new Point(1, 2);
Integer x = point.x();
Integer y = point.y();
int hashCode = point.hashCode();
String s = point.toString(); // "Point[x=1, y=2]"
assert point.equals(new Point(1, 2));
assert !point.equals(new Point(2, 2));</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_sealed_interfacesclasses_java_17">Sealed interfaces/classes (Java 17)</h3>
<div class="paragraph">
<p>With Scala&#8217;s language focus not just leverages, but was built on <em>pattern matching</em>,
having an option to control your class hierarchy is essential. Hence sealed traits (interfaces)
are essential part of scala. With it you can have fine grained control of the class hierarchy,
and have guarantees for all possible subclasses, making pattern matching even more powerful.
With the power of such features catching java&#8217;s attention, it had to adapt some of the concepts as well.</p>
</div>
<div class="paragraph">
<p>With sealed interfaces it is possible to have exact control over your class subclasses that can implement your class / interface.
With having switch expression coming only in java 21, with java 17 most of the gains is
having all the control over your class hierarchy, enabling you to enforce design principles,
and prevent unauthorized extensions.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">sealed interface Animal permits Dog, Cat {}
final class Dog implements Animal {}
final class Cat implements Animal {}
final class Bird implements Animal {}

"‘Bird' is not allowed in the sealed hierarchy"</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_java_21">Java 21</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_sequencedcollection_interface"><code>SequencedCollection</code> interface</h3>
<div class="paragraph">
<p>Before Java 21 accessing elements in order was not just not straight forward, but some brave people might call it chaos.
With some of the types not having any method to access sequential elements (like <code>getFirst</code>, <code>getLast</code>, <code>reversed</code>),
while others having the same, on top of some these' supertypes / subtypes having methods for that, while others did not.
And even the ones having methods, had separate means of calling those. In short: chaos.</p>
</div>
<div class="paragraph">
<p>With Java 21, the collections implementing sequential elements have to implement the <code>SequentialCollection</code> interface,
making the existence of these methods obvious, and unified.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 33.3333%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">def doStuff(
  list: List[Int],
  deque: ArrayDeque[Int],
  sortedSet: SortedSet[Int],
  linkedHashSet: LinkedHashSet[Int]
) = {
  var i = 0
  // get first
  i = list.head
  i = deque.head
  i = sortedSet.head
  i = linkedHashSet.head
  // get last
  i = list.last
  i = deque.last
  i = sortedSet.last
  i = linkedHashSet.last
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">void doStuff(
    List&lt;Integer&gt; list,
    Deque&lt;Integer&gt; deque,
    SortedSet&lt;Integer&gt; sortedSet,
    LinkedHashSet&lt;Integer&gt; linkedHashSet
) {
    Integer i;
    // get first
    i = list.get(0);
    i = deque.getFirst();
    i = sortedSet.first();
    i = linkedHashSet.iterator().next();
    // get last
    i = list.get(list.size() - 1);
    i = deque.getLast();
    i = sortedSet.last();
    // i = linkedHashSet.? missing
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">void doStuff2(
    List&lt;Integer&gt; list,
    Deque&lt;Integer&gt; deque,
    SortedSet&lt;Integer&gt; sortedSet,
    LinkedHashSet&lt;Integer&gt; linkedHashSet
) {
    Integer i;
    // get first
    i = list.getFirst();
    i = deque.getFirst();
    i = sortedSet.getFirst();
    i = linkedHashSet.getFirst();
    // get last
    i = list.getLast();
    i = deque.getLast();
    i = sortedSet.getLast();
    i = linkedHashSet.getLast();
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_pattern_matching_switch">Pattern matching switch</h3>
<div class="paragraph">
<p>Switch expressions were a nice syntactic sugar, but functionality wise they didn&#8217;t come with anything new.
You could still only use primitive types, Strings, or enums, and were only able to match on the concrete value of the input.
With java 21 all that changed, and you can use switch expressions with any types.
In order to actually take advantage of this of course, a lot of new patterns were introduced on top of the value match.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>null pattern<br>
In previous versions if the value was <code>null</code> inside the <code>switch</code>, it would always throw  <code>NullPointerException</code>.
With the new null pattern these exceptions can be caught.</p>
</li>
<li>
<p>type pattern<br>
You can now pattern match for the types. Just like with the pattern matching <code>instanceof</code>,
you can use the new type without any casting necessary.</p>
</li>
<li>
<p>guarded patterns<br>
You can define additional conditions for you patterns. This very well corresponds with the syntax,
previously seen with <code>instanceof</code>, where the matched arguments can already be used as a concrete type in the guards.</p>
</li>
<li>
<p>record deconstruction<br>
You can not only match on <code>records</code> similarly to the type pattern, but it is now possible to deconstruct nested structures,
and match on nested types as well. You can also use the concrete types of these nested values.
That is very powerful with deeply nested structures, where you don&#8217;t need to access a very deep value by hand.<br>
Note, that this new record deconstruction also works with the <code>instanceof</code> operator.</p>
</li>
<li>
<p>sealed class exhaustion<br>
Pattern matching switch is, where the most powerful feature of sealed classes / interfaces come to light.
Because the compiler knows exactly which classes can implement a sealed class, if the input of the switch is a sealed class,
you don&#8217;t need to define a <code>default</code> case, since the compiler is able to tell if the switch is exhaustive or not.
That way, previously existing bugs, of not updating a switch, when e.g. the domain changes are a thing in the past,
because with this new addition the compiler will force you to.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>There is no point of bringing an old example for this, since it is so different from the core,
you would simply look for another approach in previous java versions.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Scala</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">case class Wing(length: Int, fact: Option[String])

sealed trait Animal

case class Dog() extends Animal
case class Cat() extends Animal
case class Bird(birdType: String, wing: Wing) extends Animal

def getWingStatus(animal: Animal): String = animal match {
  case null =&gt; "no animal provided"
  case Dog() =&gt; "dogs don't have wings"
  case Cat() =&gt; "cats don't have wings"
  case Bird(_, Wing(_, fact)) if fact.isDefined =&gt; s"interesting fact: ${fact.get}"
  case Bird(_, Wing(_, Some(fact))) =&gt;
    s"interesting fact: ${fact}"
  case Bird(birdType, Wing(length, _)) =&gt;
    s"$birdType has $length long wings"
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">record Wing(Integer length, Optional&lt;String&gt; fact) {}

sealed interface Animal permits Dog, Cat, Bird {}

final class Dog implements Animal {}
final class Cat implements Animal {}
record Bird(String birdType, Wing wing) implements Animal {}

private String getWingStatus(Animal animal) {
    return switch (animal) {
        case null -&gt; "no animal provided";
        case Dog ignored -&gt; "dogs don't have wings";
        case Cat ignored -&gt; "cats don't have wings";
        case Bird(String birdType, Wing(Integer length, Optional&lt;String&gt; fact))
            when fact.isPresent() -&gt; "interesting fact: " + fact.get();
        case Bird(String birdType, Wing(Integer length, Optional&lt;String&gt; fact)) -&gt;
            String.format("%s has %d long wings", birdType, length);
    };
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="paragraph">
<p>You can see, how it is pretty similar now in that exact case. However one must note,
that while this is the new best thing in java, that is just the tip of the iceberg in Scala.</p>
</div>
<div class="paragraph">
<p>You can already see an example for that above, where (right before the guard pattern) the type pattern,
and the literal value pattern is mixed. First the the type <code>Bird</code> is matched,
then the record deconstructed, in a way that it will only match, if the wingfact is present.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_beyond">Beyond</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_unnamed_variables_java_22">Unnamed variables (Java 22)</h3>
<div class="paragraph">
<p>A minor improvement in developer experience is introducing unnamed variables.
If you take a look at the previous example for pattern matching, you would notice,
that although the example now looks pretty similar, in case of java you still had to give some name to the variables,
even if you are not using them later on. With the introduction of unnamed variables you can use <code>_</code> as the identifier,
and you can reuse it as many times as you&#8217;d like, and not be able to reference it later.
So it does not clutter up your scope, nor can it overshadow an outside variable by accident.
In short it doesn&#8217;t just signals the implementor&#8217;s intention, but enforces it as well,
making the code safer, and more obvious to read in the future.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">private String getWingStatus(Animal animal) {
    return switch (animal) {
        case null -&gt; "no animal provided";
        case Dog _ -&gt; "dogs don't have wings";
        case Cat _ -&gt; "cats don't have wings";
        case Bird(String _, Wing(Integer _, Optional&lt;String&gt; fact))
            when fact.isPresent() -&gt; "interesting fact: " + fact.get();
        case Bird(String birdType, Wing(Integer length, Optional&lt;String&gt; _)) -&gt;
            String.format("%s has %d long wings", birdType, length);
    };
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_unnamed_classes_java_22">Unnamed classes (Java 22)</h3>
<div class="paragraph">
<p>With previous versions already enabling running java as a single file, or even as a script,
the need for defining an exact main class is fading. This new feature leverages that thought,
and lets the user create java files without extra boilerplate.</p>
</div>
<div class="openblock scrollable">
<div class="content">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 50%;">
<col style="width: 50%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Java (old)</th>
<th class="tableblock halign-left valign-top">Java (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">public class HelloWorldJava8 {
    public static void main(String[] args) {
	    System.out.println("Hello World");
    }
}</code></pre>
</div>
</div></div></td>
<td class="tableblock halign-left valign-top"><div class="content"><div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">void main() {
    System.out.println("Hello World");
}</code></pre>
</div>
</div></div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="paragraph">
<p>This makes the learning curve much easier for a newcomer, but also opens the door to even easier scripting in the future.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_closing_thoughts">Closing thoughts</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If now, you feel like Scala is bad, because Java adopted its best features I failed.
If you feel like Java is bad, because it lacks a lot of features Scala has, I also failed.
If you feel like Java is excellent, because it can adopt in an environment, it&#8217;s maybe not that familiar,
and you feel like Scala is excellent, because it can leverage a familiar environment to the fullest,
only then I have truly succeeded.</p>
</div>
<div class="paragraph">
<p>What we need to understand, is the goal of Scala, and Java are two very different things.
Scala is a functional programming language, so the language and its whole ecosystem is designed around that fact,
while java&#8217;s main focus lies rather elsewhere.</p>
</div>
<div class="paragraph">
<p>The fact, that Java recognizes the value in a lot of functional paradigms shows the modern thinking of its maintainers,
and projects a bright future ahead.</p>
</div>
<div class="paragraph">
<p>The fact, that these paradigms are existing, and also well implemented
(even well enough for the <em>old bigbrother</em> java) shows great design from the creators of Scala.</p>
</div>
<div class="paragraph">
<p>Java and Scala, while distinct in their philosophies, have carved out a shared space within the JVM ecosystem,
proving that innovation and tradition can coexist, complement,
and even elevate each other, hence elevate modern software development.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>SBT: More than a Build Tool</title>
		<link>https://blog.lunatech.com//posts/2025-02-21-sbt-more-than-a-build-tool</link>
		
		<dc:creator><![CDATA[George]]></dc:creator>
        <pubDate>2025-02-21T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[sbt]]></category>
                }
             {
            <category><![CDATA[build-tool]]></category>
                }
             {
            <category><![CDATA[scala]]></category>
                }
             {
            <category><![CDATA[jvm]]></category>
                }
             {
            <category><![CDATA[we-know-scala]]></category>
                }
             {
            <category><![CDATA[scala-lujah]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-02-21-sbt-more-than-a-build-tool</guid>

					<description>
                        <![CDATA[ Scala Build Tool (SBT) is widely known for compiling, testing, and packaging Scala projects. However, its design as an extensible, programmable tool opens doors to uses beyond traditional build automation. Let's explore SBT's additional functionalities and practical applications.]]></description>
                    <content:encoded><![CDATA[
                    <div class="sect1">
<h2 id="_introduction">Introduction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Scala Build Tool (SBT) is widely known for compiling, testing, and packaging Scala projects. However, its design as an extensible, programmable tool opens doors to uses beyond traditional build automation. Let&#8217;s explore SBT&#8217;s additional functionalities and practical applications.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_sbt_a_brief_overview">SBT: A Brief Overview</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_what_is_sbt">What is SBT?</h3>
<div class="paragraph">
<p>SBT (Scala Build Tool) is the de facto build tool for Scala projects. It&#8217;s an open-source build tool written in Scala that provides a Domain-Specific Language (DSL) for describing build configurations. SBT offers interactive capabilities, incremental compilation, and continuous compilation features. It uses Scala code for build definitions, allowing developers to leverage the full power of the Scala language in their build processes.</p>
</div>
</div>
<div class="sect2">
<h3 id="_sbt_vs_traditional_build_tools">SBT vs Traditional Build Tools</h3>
<div class="paragraph">
<p>SBT is specifically designed for Scala projects, with deep integration into the Scala ecosystem. While it can handle Java code within Scala projects, it&#8217;s not typically used for pure Java projects where tools like Maven and Gradle are more appropriate.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_practical_applications_of_sbt">Practical Applications of SBT</h2>
<div class="sectionbody">
<div class="sect3">
<h4 id="_simple_configuration">Simple Configuration</h4>
<div class="paragraph">
<p>SBT follows "convention over configuration" principles. For basic Scala projects, you might only need a few lines in your <code>build.sbt</code> file.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">name := "my-project"
version := "1.0"
scalaVersion := "2.13.10"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_scala_based_build_definition">Scala-Based Build Definition</h4>
<div class="paragraph">
<p>Unlike XML-based build tools, SBT lets you write your build configuration in Scala. This means you can use variables, functions, and even complex logic in your build definitions, making them more maintainable and powerful.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">val commonSettings = Seq(
  organization := "com.example",
  version := "1.0",
  scalaVersion := "2.13.10"
)

lazy val core = (project in file("core"))
  .settings(commonSettings)
  .settings(
    name := "my-core-project",
    libraryDependencies += "org.typelevel" %% "cats-core" % "2.9.0"
  )</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_incremental_compilation">Incremental Compilation</h4>
<div class="paragraph">
<p>SBT tracks dependencies between your source files and only recompiles what&#8217;s necessary. When you change a file, SBT analyzes its dependencies and recompiles only affected files, significantly reducing build times.</p>
</div>
</div>
<div class="sect3">
<h4 id="_library_management">Library Management</h4>
<div class="paragraph">
<p>Using Coursier as its dependency resolver, SBT efficiently handles library dependencies, resolving and downloading them from repositories. It manages transitive dependencies and version conflicts automatically.</p>
</div>
</div>
<div class="sect3">
<h4 id="_continuous_compilation">Continuous Compilation</h4>
<div class="paragraph">
<p>With the <code>~</code> command prefix, SBT watches your source files and automatically recompiles when changes are detected. This creates a rapid development cycle, perfect for interactive development.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">// Watch and recompile
$ sbt "~compile"

// Watch and run tests
$ sbt "~test"

// Watch specific test
$ sbt "~testOnly com.example.MySpec"</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_mixed_scalajava_support">Mixed Scala/Java Support</h4>
<div class="paragraph">
<p>SBT seamlessly handles projects containing both Scala and Java code, automatically detecting and compiling both languages while maintaining proper dependency ordering.</p>
</div>
</div>
<div class="sect3">
<h4 id="_testing_framework_integration">Testing Framework Integration</h4>
<div class="paragraph">
<p>Built-in support for major Scala testing frameworks means you can run tests directly from SBT. It integrates with ScalaTest, specs2, and ScalaCheck out of the box, with plugin support for JUnit.</p>
</div>
</div>
<div class="sect3">
<h4 id="_interactive_scala_repl">Interactive Scala REPL</h4>
<div class="paragraph">
<p>Launch a Scala REPL session with your project&#8217;s classes and dependencies already loaded, perfect for exploring and testing code interactively.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">// In your project
case class User(name: String, age: Int)
class UserService {
  def greet(user: User) = s"Hello, ${user.name}!"
}

// In the REPL (after running 'sbt console')
scala&gt; val user = User("Alice", 25)
user: User = User(Alice,25)

scala&gt; val service = new UserService
service: UserService = UserService@1234abcd

scala&gt; service.greet(user)
res0: String = Hello, Alice!</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_project_modularization">Project Modularization</h4>
<div class="paragraph">
<p>Break down complex projects into manageable sub-projects, each with its own dependencies and configurations, while maintaining build coordination across the entire project.</p>
</div>
</div>
<div class="sect3">
<h4 id="_external_project_dependencies">External Project Dependencies</h4>
<div class="paragraph">
<p>Reference other Git repositories directly as dependencies, enabling seamless integration with external projects and custom forks of libraries.</p>
</div>
</div>
<div class="sect3">
<h4 id="_parallel_execution">Parallel Execution</h4>
<div class="paragraph">
<p>Speed up builds and tests by running independent tasks in parallel, taking advantage of multiple CPU cores for faster build times. By default SBT runs tasks in parallel and tests in sequence. You can change this behavior by configuring the build.sbt file by either enablig test parallelism and adjusting the parallel execution settings, such as limiting the number of cores used for example.</p>
</div>
</div>
<div class="sect2">
<h3 id="_beyond_build_tool_features">Beyond Build Tool Features</h3>
<div class="sect3">
<h4 id="_custom_task_creation">Custom Task Creation</h4>
<div class="paragraph">
<p>SBT allows you to define custom tasks for any purpose - from deploying applications to generating documentation. You can create tasks that integrate with external services, process data, or automate any development workflow.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">// Define individual tasks
lazy val startDb = taskKey[Unit]("Starts the database")
startDb := {
  "docker-compose up -d postgres".!
}

lazy val runMigrations = taskKey[Unit]("Runs database migrations")
runMigrations := Def.sequential(
  startDb,                // Start database first
  flywayClean,           // Clean database schema
  flywayMigrate          // Run Flyway migrations
).value</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_development_workflow_automation">Development Workflow Automation</h4>
<div class="paragraph">
<p>Use SBT as a complete development environment orchestrator. Create custom commands to start databases, mock services, or set up entire development environments with a single command.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">// Combine previously defined tasks into a workflow
lazy val startLocalEnv = taskKey[Unit]("Start local development environment")
startLocalEnv := Def.sequential(
  runMigrations,         // Run database migrations
  (Compile / run)        // Finally start the application
).value</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">// Use it with:
&gt; sbt startLocalEnv  // Executes all tasks in sequence</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_code_generation">Code Generation</h4>
<div class="paragraph">
<p>Leverage SBT&#8217;s source generators to automatically create code, such as generating case classes from database schemas, creating TypeScript definitions from Scala classes, or producing API documentation.</p>
</div>
</div>
<div class="sect3">
<h4 id="_database_migration">Database Migration</h4>
<div class="paragraph">
<p>Through plugins like Flyway or Slick-migration, SBT can manage database schemas and migrations, making it a powerful tool for database version control and deployment.</p>
</div>
<div class="paragraph">
<p>Using the SBT Flyway plugin:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">// In plugins.sbt
addSbtPlugin("io.github.davidmweber" % "flyway-sbt" % "7.4.0")

// In build.sbt
flywayConfigFiles := Seq("flyway-e2e.conf")</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">&gt; sbt flywayMigrate    // Using the SBT plugin</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_documentation_generation">Documentation Generation</h4>
<div class="paragraph">
<p>Beyond API docs, SBT can generate various types of documentation, from project websites to technical specifications, using plugins like sbt-site, ScalaDoc or mdoc.</p>
</div>
<div class="paragraph">
<p>A common example using ScalaDoc:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">// In build.sbt
Compile / doc / scalacOptions ++= Seq(
  "-groups",
  "-doc-title", "My Project Documentation"
)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">// Generate documentation with:
&gt; sbt doc  // Creates ScalaDoc in target/scala-2.13/api/</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_release_management">Release Management</h4>
<div class="paragraph">
<p>SBT can handle the entire release process, including version bumping, changelog generation, Git tagging, and publishing to various repositories or platforms.</p>
</div>
</div>
<div class="sect3">
<h4 id="_quality_analysis">Quality Analysis</h4>
<div class="paragraph">
<p>Integrate with code quality tools to analyze source code, check coverage, enforce styling rules, and generate quality reports as part of your development workflow.</p>
</div>
<div class="paragraph">
<p>For example, to check code coverage in your project, first add the scoverage plugin to your <code>project/plugins.sbt</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9")</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then you can run coverage analysis:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">&gt; sbt coverage         // Enable code coverage tracking
&gt; sbt test            // Run your tests - this collects coverage data
&gt; sbt coverageReport  // Generate coverage report showing which code was tested</code></pre>
</div>
</div>
<div class="paragraph">
<p>The report will be generated in <code>target/scala-2.13/scoverage-report/</code> and includes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>HTML reports showing line-by-line coverage</p>
</li>
<li>
<p>Overall coverage statistics</p>
</li>
<li>
<p>Highlighted source code showing covered/uncovered lines</p>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>SBT is a powerful tool that transcends its role as a build tool, offering developers a versatile platform for managing, automating, and enhancing their development workflows. Whether you’re working on a small library or a large-scale application, SBT’s features and extensibility make it a valuable addition to the Scala ecosystem. SBT acts more as a development platform than a build tool and by understanding its capabilities and limitations, teams can leverage SBT to streamline their processes and focus on building great software.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
         
	<item>
		<title>Interop Summit. Why do we only import Java libraries?</title>
		<link>https://blog.lunatech.com//posts/2025-02-14-interop-summit</link>
		
		<dc:creator><![CDATA[Konstantin Kolmar]]></dc:creator>
        <pubDate>2025-02-14T00:00:00.000Z</pubDate>
         
             {
            <category><![CDATA[jvm]]></category>
                }
             {
            <category><![CDATA[interop]]></category>
                }
             {
            <category><![CDATA[scala]]></category>
                }
             {
            <category><![CDATA[scala3]]></category>
                }
             {
            <category><![CDATA[kotlin]]></category>
                }
             {
            <category><![CDATA[gradle]]></category>
                }
             {
            <category><![CDATA[groovy]]></category>
                }
             {
            <category><![CDATA[we-know-scala]]></category>
                }
             {
            <category><![CDATA[scala-lujah]]></category>
                }
             {
            <category><![CDATA[en]]></category>
                }
             
        
		<guid isPermaLink="false">https://blog.lunatech.com//posts/2025-02-14-interop-summit</guid>

					<description>
                        <![CDATA[ The Java platform, including the Java language and Java Virtual Machine (JVM), was first released around 1995. At that time, Java was the only language running on the JVM. Soon, however, other languages followed, which addressed some of Java’s shortcomings while still leveraging its runtime and the vast collection of standard and third-party libraries and allowing integration with a large amount of existing Java code.]]></description>
                    <content:encoded><![CDATA[
                    <div class="sect1">
<h2 id="_short_history_of_the_java_platform">Short history of the Java platform</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Java platform, including the Java language and Java Virtual Machine (JVM), was first released around 1995. At that time, Java was the only language running on the JVM. Soon, however, other languages followed, which addressed some of Java’s shortcomings while still leveraging its runtime and the vast collection of standard and third-party libraries and allowing integration with a large amount of existing Java code.</p>
</div>
<div class="paragraph">
<p>Scala was one of the first such languages. Its development started in 2001, and the first release came out in 2004. Its main goal was to mix functional and object-oriented programming and to allow writing more concise code than Java.</p>
</div>
<div class="paragraph">
<p>Groovy was released in 2007 and introduced dynamic typing and improved scripting capabilities. Clojure also came out in 2007. As a Lisp dialect, its strengths lie in functional programming and metaprogramming.</p>
</div>
<div class="paragraph">
<p>Kotlin was one of the later major languages that entered the scene. Its implementation started in 2010, and the first stable release was in 2016. Kotlin was designed to be a nicer Java with seamless interoperability. Later, it became officially supported for Android development.</p>
</div>
<div class="paragraph">
<p>It is interesting to note that no new major languages have appeared in about 15 years now. This is probably because existing languages already cover the most important niches. Moreover, Java now includes quality-of-life features like lambdas, closures, lazy streams, records, and virtual threads, which had previously motivated the creation of new languages. Nowadays you would also need a large amount of tooling and libraries to support a new language, and probably some killer application or support and promotion from a large company.</p>
</div>
<div class="paragraph">
<p>Still, there is a large variety of less prominent languages on the JVM, including even some internal company languages. Many originally non-JVM languages have compilers that target JVM such as Jython for Python.</p>
</div>
<div class="paragraph">
<p>But given the large variety of available options, why do we only import libraries implemented in Java or in the language we are using? Well, it&#8217;s probably not a huge mystery. I bet you can name several important reasons yourself. But I think it&#8217;s still interesting to think about, so let&#8217;s review some of them.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_1_utility_libraries_are_implemented_in_each_language">1. Utility libraries are implemented in each language</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Small or utility libraries are simply implemented natively in each language to provide idiomatic APIs. Such implementations might be a part of the standard library or provided by third parties. A good example is JSON processing. There are a lot of third-party JSON libraries in Scala, often using typeclasses and other special Scala features. In contrast, in Kotlin JSON is supported by an official library in <a href="https://github.com/Kotlin/kotlinx.serialization">kotlinx.serialization</a>. There is a similar situation with idiomatic libraries for dependency injection, web frameworks, and so on.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_2_few_large_unique_libraries">2. Few large unique libraries</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There is a lack of genuine need to import anything large from outside. What libraries unique to Kotlin, let alone other non-Java languages, would you want to use in your Scala code? Jetpack Compose is an example of a Kotlin library occupying a unique niche. It is the de facto default GUI framework on Android. However, since it&#8217;s a GUI framework you wouldn’t normally import it as a library, and you rarely see Scala code on Android anyway.</p>
</div>
<div class="paragraph">
<p>As for Scala, there are unique libraries that could be useful in Java or Kotlin code: Akka or Pekko, Spark, Gatling, or Flink. But they usually already provide bindings or APIs for Java, so you don&#8217;t need their Scala APIs. Many of them are even discontinuing their Scala API, or avoiding migration to Scala 3.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_3_focus_on_using_java_code">3. Focus on using Java code</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Java is one of the most popular languages ever created, so over its long history a lot of libraries for all kinds of purposes have been produced. This means that language authors of every JVM language make a special effort to provide an easy way to interact with Java dependencies.</p>
</div>
<div class="paragraph">
<p>The end result is that using Java code from Scala or Kotlin is fairly simple. You can call methods normally and give them arguments, extend classes and interfaces, and so on. Scala standard library supplies a <code>scala.jdk</code> package, that contains many facilities to assist with interoperability.</p>
</div>
<div class="paragraph">
<p>Of course, this is still not always completely straightforward. One obvious point of contention is nullability. When using Java libraries from Scala you usually need to wrap nearly everything in an <code>Option</code> to avoid null pointer exceptions. You may say that using Java libraries is not idiomatic, but even in the standard library, there are packages you may want to use occasionally: <code>java.nio.file</code>, <code>java.time</code>, Unicode support, and so on.</p>
</div>
<div class="paragraph">
<p>Here is an example of a Scala 3 extension to wrap the result of one Java method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">extension (p: Path)
  def parent: Option[Path] = Option(p.getParent)</code></pre>
</div>
</div>
<div class="paragraph">
<p>This approach is not ideal, because if you miss any wrappers, you&#8217;ll get a <code>NullPointerException</code> at runtime.</p>
</div>
<div class="paragraph">
<p>Scala 3 has added union types, so now you can declare nullable types directly: <code>MyType | Null</code>. It also has the <a href="https://docs.scala-lang.org/scala3/reference/experimental/explicit-nulls.html">explicit nulls</a> feature enabled by the <code>-Yexplicit-nulls</code> flag. This means that all values that can have a <code>null</code> value, including all values coming from Java, must have a nullable type. This reduces the <code>Option</code> boilerplate when dealing with Java code, but introduces a different kind of boilerplate: null-checking the results of functions that can never return null.</p>
</div>
<div class="paragraph">
<p>To improve this the recently released Scala 3.5 has introduced <a href="https://docs.scala-lang.org/scala3/reference/experimental/explicit-nulls.html#java-interoperability-and-flexible-types-1">flexible types</a> inspired by Kotlin&#8217;s <a href="https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types">platform types</a>. This feature is enabled by default but can be turned off with the <code>-Yno-flexible-types</code> flag. It means that values coming from Java can now be treated as either nullable or non-nullable. This offers the same safety guarantees as Java, so again it has reverted to the <code>Option</code> wrapper situation, and it&#8217;s possible to get null pointer exceptions again.</p>
</div>
<div class="paragraph">
<p>This development process demonstrates both how much effort forward interop receives and how hard it is to provide a good solution for some issues. Well, nulls wouldn&#8217;t have been a billion-dollar mistake otherwise.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_4_reverse_interoperability_is_complicated">4. Reverse interoperability is complicated</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Reverse interoperability refers to tools and techniques that allow developers to write code accessible from Java and potentially other JVM languages. However, it can be difficult to transparently incorporate features that are unique to a particular language. For instance, when calling Scala code from Java, implicit values must be explicitly constructed, which undermines the usability of the API.</p>
</div>
<div class="paragraph">
<p>Even in simpler cases reverse interoperability requires carefully marking your code with annotations or using some non-idiomatic constructs. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">case class MountainRange(mountains: List[Mountain])
object MountainRange:
  @varargs
  @targetName("of3")
  @throws[IllegalArgumentException]
  def ^^^(mountains: Mountain*): MountainRange = {
    if (mountains.size != 3) throw IllegalArgumentException("Incorrect number of mountains")
    MountainRange(mountains.toList)
  }</code></pre>
</div>
</div>
<div class="paragraph">
<p>The annotations in this method cause the Scala compiler to produce a signature that can better interact with Java. <code>@varargs</code> means that the argument will be a Java <code>Array</code> instead of a Scala <code>Seq</code>, <code>targetName</code> gives it a stable alphanumeric name instead of a technical name mangled by the compiler, and <code>@throws</code> adds the corresponding "throws" declaration to the method signature.</p>
</div>
<div class="paragraph">
<p>Another interesting example that demonstrates how much of an afterthought reverse interop can be is trying to define an enumeration that extends <code>java.lang.Enum</code>. In Scala 3 this is straightforward:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-scala" data-lang="scala">enum Mountain extends Enum[Mountain]:
  case Everest, Kilimanjaro, MontBlanc, Fujiyama</code></pre>
</div>
</div>
<div class="paragraph">
<p>Scala 3 compiler automatically generates calls to the <code>java.lang.Enum</code> constructor to initialize the values. But I don&#8217;t know a way to create a Java-compatible enumeration from Scala 2. Maybe it&#8217;s not possible at all!</p>
</div>
<div class="paragraph">
<p>Other features reverse interoperability has to handle may include:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Static methods and constant values.</p>
</li>
<li>
<p>Class fields, getters, and setters, because many JVM languages try to improve their handling in some way.</p>
</li>
<li>
<p>Optional method arguments and default values. Java doesn&#8217;t allow those, but most other JVM languages do.</p>
</li>
<li>
<p>Different visibility modes, such as Scala&#8217;s <code>sealed</code> or Kotlin&#8217;s <code>internal</code>.</p>
</li>
<li>
<p>Concurrency primitives. Every language has something unique and barely compatible with each other. For example, Scala uses Futures or various IO libraries, Kotlin has coroutines in its standard library, and Java has recently added virtual threads.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Imagine having to support this menagerie for multiple languages, each with its own assumptions and idioms, changing and updating over time. If every language provided bindings or APIs for every other language, the complexity would explode.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_5_concerns_about_runtime_dependencies">5. Concerns about runtime dependencies</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Using libraries from another language usually implies including that language&#8217;s standard library as a runtime dependency. This slows down the build and increases distribution sizes. The effect may not be large in absolute terms, but still provides enough incentive for library authors to design their libraries to avoid unnecessary dependencies on the entire standard library of a whole language.</p>
</div>
<div class="paragraph">
<p>As a consequence of those reasons Java naturally serves as the common denominator to mediate between JVM languages.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_case_study">Case study</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Situations where you need to interact between non-Java languages do happen but are fairly unusual. One interesting example from our team involved configuring access to intranet repositories (without internet access) in our Gradle builds.</p>
</div>
<div class="paragraph">
<p>Let&#8217;s adopt the following assumptions:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>We are using Kotlin for our Gradle builds, because Kotlin is statically typed and its tooling and IDE support are better than Groovy’s.</p>
</li>
<li>
<p>Multiple teams publish their artifacts into their own repositories on the shared Artifactory. Different projects need different dependencies, so our goal is to give the project maintainers a simple way to add the repositories with the artifacts they need. We want to have an extension method on the <code>RepositoryHandler</code>, similar to the idiomatic Gradle methods such as <code>mavenCentral()</code> or <code>gradlePluginPortal()</code>:</p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-kotlin" data-lang="kotlin">repositories { // this: RepositoryHandler =&gt;
    mavenInternal("maven-releases")
    mavenInternal("gradle-plugins")
    mavenInternal("other-team-artifacts")
}</code></pre>
</div>
</div>
</li>
<li>
<p>We have a local plugin to set the repository URL and configure a way to obtain a login token from the environment:</p>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-groovy" data-lang="groovy">def extendRepositories(RepositoryHandler repositories) {
    if (repositories !instanceof ExtensionAware) return

    repositories.ext {
        mavenInternal = { repoName -&gt;
            repositories.maven {
                name = repoName
                url = "https://artifactory.example.com/$repoName"
                credentials {
                    token = providers.environmentVariable("ARTIFACTORY_TOKEN")
                            .orElse(providers.systemProperty("gradle.wrapperPassword"))
                            .orNull
                }
            }
        }
    }
}</code></pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>The problem here is that Gradle can automatically execute Groovy builds, but for Kotlin builds it needs to download a special plugin, and to download the plugin without internet access, it requires the internal repository to be already configured, creating a Catch-22 type of problem. This means the repository configuration plugin above has to be implemented in Groovy.</p>
</div>
<div class="paragraph">
<p>In the Groovy build flavor, you can directly use methods defined in an <a href="https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html">extra properties extension</a>. But Kotlin doesn&#8217;t understand that approach. It can&#8217;t interact with standard Groovy extension methods either. Groovy implements them by modifying Groovy metaclasses, but in Kotlin extension methods are just syntax sugar, and at runtime are implemented as normal methods taking the receiver as the first argument.</p>
</div>
<div class="paragraph">
<p>In the end, the solution was to create an intermediate plugin in Kotlin, that provides a Kotlin-style extension method. It extracts Groovy <code>Closure</code> from the extra properties extension, casts it to the appropriate type, and calls it using Groovy API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-kotlin" data-lang="kotlin">fun RepositoryHandler.mavenInternal(path: String) {
    ((this as ExtensionAware).extra["mavenInternal"] as Closure&lt;*&gt;).call(path)
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is still not ideal, because this helper method can&#8217;t be shared between the intermediate plugin build and implementation, so it has to be copy-pasted into several places. Nevertheless, this achieves the goal of having nice repository declarations in the user-level Kotlin build.</p>
</div>
<div class="paragraph">
<p>This is an example of how convoluted interoperability can look when assumptions and idioms from different languages and libraries come in conflict with each other.</p>
</div>
</div>
</div>
<]]></content:encoded>
					
		
		
			</item>
        
	</channel>
</rss>


