<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Base42]]></title><description><![CDATA[42.mk - The answer to life, the universe and everything. We just forgot what the question was. Open Source community, globally available started in Macedonia.]]></description><link>https://blog.42.mk</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1710607544352/04ibOyY9g.png</url><title>Base42</title><link>https://blog.42.mk</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 16:13:34 GMT</lastBuildDate><atom:link href="https://blog.42.mk/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Migrating from Next.js to SolidStart: An Opinionated Guide]]></title><description><![CDATA[If you're looking for alternatives to Next.js - whether for ideological, practical, or curiosity-driven reasons - I think SolidStart is one of the best options out there. SolidStart is to SolidJS what]]></description><link>https://blog.42.mk/migrating-from-next-js-to-solidstart-an-opinionated-guide</link><guid isPermaLink="true">https://blog.42.mk/migrating-from-next-js-to-solidstart-an-opinionated-guide</guid><dc:creator><![CDATA[Pavlina Buchevska]]></dc:creator><pubDate>Tue, 07 Apr 2026 13:05:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/c983cbd7-2c84-480f-866b-e6800c9551bb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're looking for alternatives to Next.js - whether for ideological, practical, or curiosity-driven reasons - I think SolidStart is one of the best options out there. SolidStart is to <a href="https://www.solidjs.com/">SolidJS</a> what Next.js is to React: the metaframework that adds routing, SSR, server functions, and deployment on top of the UI library. This guide covers both layers - the primitives and the metaframework - so you can evaluate whether it's the right move.</p>
<p>But first, the elephant in the room: <strong>Solid is not React.</strong> It <em>looks</em> like React, and that's both a blessing and a trap.</p>
<h2>The one thing you need to internalize</h2>
<blockquote>
<p><strong>In Solid, the component function is a constructor, not a render function.</strong></p>
</blockquote>
<p>In React, your component function re-runs on every state change. Every. Single. Time. Hooks, variables, derived values - all re-evaluated. In Solid, the component function runs <strong>once</strong>. It sets things up, wires the reactive graph together, and never runs again. All subsequent updates happen through signal subscriptions that surgically update the specific DOM nodes that care about the change.</p>
<p>Once you truly get this, every other difference clicks into place. Signals are getter functions because you need <em>live references</em>. Effects auto-track because they subscribe at call-time. Props can't be destructured because that reads values eagerly outside a tracking scope. <code>&lt;Show&gt;</code> and <code>&lt;For&gt;</code> exist because there's no virtual DOM to diff.</p>
<p>Let that sink in. Now let's get practical.</p>
<hr />
<h2>Solid vs. React: the primitives</h2>
<p>On the surface, Solid and React feel similar - which is a good thing if you're coming from React land. Both use <a href="https://en.wikipedia.org/wiki/JavaScript_XML">JSX</a> to output HTML. But the similarities are surface-level. How they handle state, effects, iteration, conditional rendering, and lifecycle is fundamentally different.</p>
<p>Let's go through each, side by side.</p>
<h3>1. State: <code>useState</code> vs <code>createSignal</code></h3>
<p><strong>React:</strong></p>
<pre><code class="language-jsx">function Counter() {
  // This entire function re-runs on every setCount call
  const [count, setCount] = useState(0);
  console.log("rendered"); // fires every time

  return &lt;button onClick={() =&gt; setCount(count + 1)}&gt;
    {count}
  &lt;/button&gt;;
}
</code></pre>
<p><strong>Solid:</strong></p>
<pre><code class="language-jsx">function Counter() {
  // This function runs ONCE
  const [count, setCount] = createSignal(0);
  console.log("setup"); // fires exactly once

  return &lt;button onClick={() =&gt; setCount(count() + 1)}&gt;
    {count()}
  &lt;/button&gt;;
}
</code></pre>
<p>The gotcha that'll bite you first: <code>count</code> <strong>is a function in Solid.</strong> You call <code>count()</code> to read the value. Writing <code>{count}</code> without the parentheses will render the function reference itself - not the number. This is, by far, the most common mistake React developers make when starting with Solid.</p>
<h3>2. Effects: <code>useEffect</code> vs <code>createEffect</code></h3>
<p>React requires you to manually declare dependencies. Solid tracks them automatically.</p>
<p><strong>React:</strong></p>
<pre><code class="language-jsx">function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() =&gt; {
    let cancelled = false;
    fetchUser(userId).then(data =&gt; {
      if (!cancelled) setUser(data);
    });
    return () =&gt; { cancelled = true; }; // cleanup via return
  }, [userId]); // manual deps - forget this and you have a bug

  return &lt;div&gt;{user?.name}&lt;/div&gt;;
}
</code></pre>
<p><strong>Solid:</strong></p>
<pre><code class="language-jsx">function UserProfile(props) {
  const [user, setUser] = createSignal(null);

  createEffect(() =&gt; {
    // Solid auto-tracks props.userId - no deps array
    const controller = new AbortController();
    fetchUser(props.userId, { signal: controller.signal }).then(setUser);

    onCleanup(() =&gt; controller.abort()); // cleanup via onCleanup()
  });

  return &lt;div&gt;{user()?.name}&lt;/div&gt;;
}
</code></pre>
<p>Key differences to watch for:</p>
<ul>
<li><p><strong>No deps array.</strong> Solid tracks what you read inside the effect automatically. If it reads <code>props.userId</code>, it re-runs when that changes. Period.</p>
</li>
<li><p><strong>Cleanup uses</strong> <code>onCleanup()</code><strong>,</strong> not a return value. Import it from <code>solid-js</code>.</p>
</li>
<li><p><strong>No empty deps array pattern.</strong> In React, <code>useEffect(() =&gt; {...}, [])</code> means "run once on mount." In Solid, use <code>onMount()</code> for that.</p>
</li>
<li><p><strong>Timing is different.</strong> <code>createEffect</code> runs synchronously after the reactive graph updates - closer in spirit to React's <code>useLayoutEffect</code> than <code>useEffect</code>, though the exact timing relative to browser paint depends on when the signal change was triggered. If you need explicit dependency control, Solid has <code>on()</code>:</p>
</li>
</ul>
<pre><code class="language-jsx">import { on } from "solid-js";

createEffect(on(count, (value, prev) =&gt; {
  console.log("count changed from", prev, "to", value);
}));
</code></pre>
<h3>3. Memos: <code>useMemo</code> vs <code>createMemo</code></h3>
<p>React's <code>useMemo</code> is <a href="https://react.dev/reference/react/useMemo">a performance optimization, not a semantic guarantee</a> - React may discard cached values. Solid's <code>createMemo</code> is a reactive primitive that creates a derived signal. It's guaranteed to be lazy and cached.</p>
<p><strong>React:</strong></p>
<pre><code class="language-jsx">function FilteredList({ items, filter }) {
  const filtered = useMemo(
    () =&gt; items.filter(i =&gt; i.category === filter),
    [items, filter]
  );
  return &lt;ul&gt;{filtered.map(i =&gt; &lt;li key={i.id}&gt;{i.name}&lt;/li&gt;)}&lt;/ul&gt;;
}
</code></pre>
<p><strong>Solid:</strong></p>
<pre><code class="language-jsx">function FilteredList(props) {
  const filtered = createMemo(() =&gt;
    props.items.filter(i =&gt; i.category === props.filter)
  );

  return &lt;ul&gt;
    &lt;For each={filtered()}&gt;{i =&gt; &lt;li&gt;{i.name}&lt;/li&gt;}&lt;/For&gt;
  &lt;/ul&gt;;
}
</code></pre>
<p>Same deal: <code>createMemo</code> returns a getter function - call <code>filtered()</code>, not <code>filtered</code>.</p>
<h3>4. Conditional rendering: ternaries vs <code>&lt;Show&gt;</code></h3>
<p>In React, ternaries work because the virtual DOM diffs everything anyway - it papers over the cost for you. In Solid, there's no virtual DOM hiding that cost. Raw ternaries can cause DOM nodes to be torn down and recreated on every toggle. <code>&lt;Show&gt;</code> gives you efficient conditional rendering without the diffing overhead.</p>
<p><strong>React:</strong></p>
<pre><code class="language-jsx">function Greeting({ user }) {
  return (
    &lt;div&gt;
      {user ? (
        &lt;h1&gt;Welcome back, {user.name}!&lt;/h1&gt;
      ) : (
        &lt;h1&gt;Please sign in.&lt;/h1&gt;
      )}
    &lt;/div&gt;
  );
}
</code></pre>
<p><strong>Solid:</strong></p>
<pre><code class="language-jsx">function Greeting(props) {
  return (
    &lt;div&gt;
      &lt;Show when={props.user} fallback={&lt;h1&gt;Please sign in.&lt;/h1&gt;}&gt;
        {(user) =&gt; &lt;h1&gt;Welcome back, {user().name}!&lt;/h1&gt;}
      &lt;/Show&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>The callback child form <code>{(user) =&gt; ...}</code> gives you a narrowed, guaranteed-truthy value. But note: <code>user</code> is a getter function in Solid, hence <code>user().name</code>.</p>
<p>For multiple conditions, <code>&lt;Switch&gt;</code> / <code>&lt;Match&gt;</code> is your friend:</p>
<pre><code class="language-jsx">&lt;Switch fallback={&lt;p&gt;Not found&lt;/p&gt;}&gt;
  &lt;Match when={state() === "loading"}&gt;&lt;Spinner /&gt;&lt;/Match&gt;
  &lt;Match when={state() === "error"}&gt;&lt;ErrorView /&gt;&lt;/Match&gt;
  &lt;Match when={state() === "ready"}&gt;&lt;Content /&gt;&lt;/Match&gt;
&lt;/Switch&gt;
</code></pre>
<h3>5. List rendering: <code>.map()</code> vs <code>&lt;For&gt;</code></h3>
<p>In React, <code>.map()</code> recreates the virtual DOM array every render, then diffs with keys. In Solid, <code>&lt;For&gt;</code> tracks each item by reference - only affected DOM nodes get created, moved, or removed.</p>
<p><strong>React:</strong></p>
<pre><code class="language-jsx">function TodoList({ todos }) {
  return (
    &lt;ul&gt;
      {todos.map(todo =&gt; (
        &lt;li key={todo.id}&gt;{todo.text}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}
</code></pre>
<p><strong>Solid:</strong></p>
<pre><code class="language-jsx">function TodoList(props) {
  return (
    &lt;ul&gt;
      &lt;For each={props.todos}&gt;
        {(todo, index) =&gt; &lt;li&gt;{todo.text}&lt;/li&gt;}
      &lt;/For&gt;
    &lt;/ul&gt;
  );
}
</code></pre>
<p>A couple of things:</p>
<ul>
<li><p><strong>No</strong> <code>key</code> <strong>prop needed.</strong> <code>&lt;For&gt;</code> tracks by reference automatically.</p>
</li>
<li><p><code>index</code> <strong>is a signal.</strong> Call <code>index()</code> to get the number. The item itself (<code>todo</code>) is <em>not</em> a signal - it's the raw value.</p>
</li>
<li><p>Using <code>.map()</code> in Solid <em>works</em> - it won't crash your app - but it creates a non-keyed list that re-renders everything on every update. Essentially, it makes Solid behave like a slow React app (and if you do, I have questions). <code>&lt;For&gt;</code> is the correct tool for the job.</p>
</li>
</ul>
<h3>6. Refs: <code>useRef</code> vs… just a variable</h3>
<p>This one's refreshingly simple.</p>
<p><strong>React:</strong></p>
<pre><code class="language-jsx">function AutoFocus() {
  const inputRef = useRef(null);

  useEffect(() =&gt; {
    inputRef.current.focus();
  }, []);

  return &lt;input ref={inputRef} /&gt;;
}
</code></pre>
<p><strong>Solid:</strong></p>
<pre><code class="language-jsx">function AutoFocus() {
  let inputRef;

  onMount(() =&gt; {
    inputRef.focus(); // no .current - it IS the element
  });

  return &lt;input ref={inputRef} /&gt;;
}
</code></pre>
<p>No <code>.current</code> indirection. No hook. Just a variable. Since Solid components run once, a plain <code>let</code> persists for the component's lifetime. This also means you don't need <code>useRef</code> as a "mutable box" - any <code>let</code> variable already serves that purpose.</p>
<h3>7. Props: the destructuring trap</h3>
<p>This is the gotcha that bites hardest, because it works perfectly in React.</p>
<p><strong>React - destructuring is totally safe:</strong></p>
<pre><code class="language-jsx">function Greeting({ name, age }) {
  return &lt;p&gt;{name} is {age} years old&lt;/p&gt;;
}
</code></pre>
<p><strong>Solid - destructuring BREAKS reactivity:</strong></p>
<pre><code class="language-jsx">// BAD - values captured once, never update
function Greeting({ name, age }) {
  return &lt;p&gt;{name} is {age} years old&lt;/p&gt;;
}

// GOOD - reactive access through the props proxy
function Greeting(props) {
  return &lt;p&gt;{props.name} is {props.age} years old&lt;/p&gt;;
}
</code></pre>
<p>Why? Solid props are a Proxy. Accessing <code>props.name</code> inside JSX creates a reactive subscription. Destructuring eagerly reads the values during component setup (which runs once), severing the reactive connection forever.</p>
<p>When you need defaults or to split props for forwarding, use <code>mergeProps</code> and <code>splitProps</code>:</p>
<pre><code class="language-jsx">import { splitProps, mergeProps } from "solid-js";

function Button(props) {
  const merged = mergeProps({ variant: "primary", size: "md" }, props);
  const [local, rest] = splitProps(merged, ["variant", "size"]);

  return (
    &lt;button class={`btn-\({local.variant} btn-\){local.size}`} {...rest}&gt;
      {props.children}
    &lt;/button&gt;
  );
}
</code></pre>
<h3>8. Building your own primitives</h3>
<p>This is where Solid's "primitives, not frameworks" philosophy really pays off. In React, custom hooks are functions that call other hooks - but they re-run on every render, so you're always thinking about memoization and stale closures. In Solid, a custom primitive is just a function that wires up signals - it runs once, and the reactive graph handles the rest.</p>
<p>Here's a practical example: a <code>createFetch</code> primitive that handles loading, error, success, and data states.</p>
<pre><code class="language-jsx">import { createSignal, createEffect, onCleanup } from "solid-js";

function createFetch(urlFn) {
  const [data, setData] = createSignal(null);
  const [error, setError] = createSignal(null);
  const [loading, setLoading] = createSignal(true);

  createEffect(() =&gt; {
    const url = urlFn(); // tracked - re-runs when URL changes
    const controller = new AbortController();

    setLoading(true);
    setError(null);

    fetch(url, { signal: controller.signal })
      .then((res) =&gt; {
        if (!res.ok) throw new Error(`\({res.status} \){res.statusText}`);
        return res.json();
      })
      .then((json) =&gt; {
        setData(() =&gt; json);
        setLoading(false);
      })
      .catch((err) =&gt; {
        if (err.name !== "AbortError") {
          setError(() =&gt; err);
          setLoading(false);
        }
      });

    onCleanup(() =&gt; controller.abort());
  });

  return { data, error, loading };
}
</code></pre>
<p>Use it in a component:</p>
<pre><code class="language-jsx">function UserProfile(props) {
  const { data, error, loading } = createFetch(
    () =&gt; `/api/users/${props.userId}`
  );

  return (
    &lt;Switch&gt;
      &lt;Match when={loading()}&gt;
        &lt;Spinner /&gt;
      &lt;/Match&gt;
      &lt;Match when={error()}&gt;
        &lt;p&gt;Error: {error().message}&lt;/p&gt;
      &lt;/Match&gt;
      &lt;Match when={data()}&gt;
        {/* data() is the resolved JSON */}
        &lt;h1&gt;{data().name}&lt;/h1&gt;
        &lt;p&gt;{data().email}&lt;/p&gt;
      &lt;/Match&gt;
    &lt;/Switch&gt;
  );
}
</code></pre>
<p>Notice what's <em>not</em> here: no dependency arrays, no memoization, no stale closure bugs. The URL is a getter function (<code>urlFn()</code>), so it's tracked automatically. When <code>props.userId</code> changes, the effect re-runs, the previous request is aborted, and the new one fires. All of this wired up in a reusable primitive that composes like a building block.</p>
<p>This is the pattern you'll reach for constantly in Solid - extract reactive logic into composable primitives that return signals. The <a href="https://primitives.solidjs.community/">Solid Primitives</a> community library is built entirely on this idea.</p>
<hr />
<h2>Quick reference cheat sheet</h2>
<table>
<thead>
<tr>
<th>Concept</th>
<th>React</th>
<th>Solid</th>
<th>Gotcha</th>
</tr>
</thead>
<tbody><tr>
<td>State</td>
<td><code>const [v, setV] = useState(0)</code></td>
<td><code>const [v, setV] = createSignal(0)</code></td>
<td><code>v()</code> not <code>v</code></td>
</tr>
<tr>
<td>Effects</td>
<td><code>useEffect(() =&gt; {}, [deps])</code></td>
<td><code>createEffect(() =&gt; {})</code></td>
<td>No deps array; <code>onCleanup</code> for cleanup</td>
</tr>
<tr>
<td>Memo</td>
<td><code>useMemo(() =&gt; x, [deps])</code></td>
<td><code>createMemo(() =&gt; x)</code></td>
<td>Returns getter: <code>memo()</code></td>
</tr>
<tr>
<td>Refs</td>
<td><code>useRef(null)</code> + <code>.current</code></td>
<td><code>let ref;</code></td>
<td>No <code>.current</code></td>
</tr>
<tr>
<td>Conditionals</td>
<td><code>{cond ? &lt;A/&gt; : &lt;B/&gt;}</code></td>
<td><code>&lt;Show when={cond}&gt;...&lt;/Show&gt;</code></td>
<td>Callback child for narrowing</td>
</tr>
<tr>
<td>Lists</td>
<td><code>arr.map(x =&gt; &lt;X key={id}/&gt;)</code></td>
<td><code>&lt;For each={arr}&gt;{x =&gt; &lt;X/&gt;}&lt;/For&gt;</code></td>
<td>No key; index is a signal</td>
</tr>
<tr>
<td>Props</td>
<td>Destructure freely</td>
<td><strong>Never destructure</strong></td>
<td>Use <code>splitProps</code> / <code>mergeProps</code></td>
</tr>
<tr>
<td>Component fn</td>
<td>Re-runs every render</td>
<td>Runs once</td>
<td>Derived values need <code>() =&gt;</code> wrapper</td>
</tr>
</tbody></table>
<hr />
<h2>Next.js vs SolidStart: the metaframework layer</h2>
<p>With the primitives covered, let's zoom out. You're not just migrating from React to Solid - you're migrating from Next.js to SolidStart. That means routing, SSR, server-side code execution, API endpoints, data fetching, and all the other metaframework concerns. This is the part where I could just give you a table and call it a day. But some of these differences are subtle enough so that it warrants drawing a clearer line between the two.</p>
<h3>Routing</h3>
<p>Both frameworks use file-based routing, but the conventions differ.</p>
<p><strong>Next.js (App Router):</strong></p>
<pre><code class="language-plaintext">app/
  page.tsx            → /
  blog/
    page.tsx          → /blog
    [slug]/
      page.tsx        → /blog/:slug
  (marketing)/
    about/
      page.tsx        → /about
  layout.tsx          → root layout
  loading.tsx         → loading UI
  error.tsx           → error boundary
</code></pre>
<p>Next.js uses special file conventions - <code>page.tsx</code>, <code>layout.tsx</code>, <code>loading.tsx</code>, <code>error.tsx</code>, <code>not-found.tsx</code> - each with specific roles. Route groups use parentheses <code>(group)</code>. Layouts are implicit: a <code>layout.tsx</code> wraps all pages in its directory and below. Dynamic segments use <code>[param]</code>, catch-all uses <code>[...param]</code>.</p>
<p><strong>SolidStart:</strong></p>
<pre><code class="language-plaintext">routes/
  index.tsx           → /
  blog/
    index.tsx         → /blog
    [slug].tsx        → /blog/:slug
  about.tsx           → /about
  blog.tsx            → layout for /blog/*
</code></pre>
<p>SolidStart keeps it simpler. A file's name <em>is</em> the route - no <code>page.tsx</code> convention. Dynamic segments use <code>[param]</code>, optional params use <code>[[param]]</code>, catch-all uses <code>[...param]</code>. Layouts work by naming a file the same as a directory - <code>blog.tsx</code> alongside a <code>blog/</code> folder makes <code>blog.tsx</code> the layout. Child routes render via <code>props.children</code>:</p>
<pre><code class="language-jsx">// routes/blog.tsx - layout for all /blog/* routes
export default function BlogLayout(props) {
  return (
    &lt;div class="blog-wrapper"&gt;
      &lt;nav&gt;Blog nav here&lt;/nav&gt;
      {props.children}
    &lt;/div&gt;
  );
}
</code></pre>
<p>Route groups use parentheses <code>(group)/</code>, same idea as Next.js - directories wrapped in <code>()</code> that organize routes without affecting URL structure.</p>
<p><strong>The key difference:</strong> Next.js has more special files with implicit behavior (loading states, error boundaries, not-found pages are all file-convention driven). SolidStart gives you the routing structure and lets you compose <code>&lt;Suspense&gt;</code>, <code>&lt;ErrorBoundary&gt;</code>, and <code>&lt;Show&gt;</code> yourself. Fewer conventions to memorize, more explicit control. 8/10 physicians agree that less magic in your code can have a positive impact on one's mental health.</p>
<h3>SSR and rendering modes</h3>
<p><strong>Next.js</strong> gives you a buffet of rendering strategies:</p>
<ul>
<li><p><strong>Static (SSG)</strong> - pages pre-rendered at build time</p>
</li>
<li><p><strong>Server-side (SSR)</strong> - rendered on each request</p>
</li>
<li><p><strong>Incremental Static Regeneration (ISR)</strong> - static pages that revalidate after a set time</p>
</li>
<li><p><strong>Partial Pre-rendering (PPR)</strong> - static shell with streaming dynamic holes (React 19.2)</p>
</li>
<li><p><strong>React Server Components</strong> - components that run on the server and ship zero client JS</p>
</li>
</ul>
<p>The rendering mode is determined by what you do in the component. Use <code>"use cache"</code>? Cached. Read <code>cookies()</code> or <code>headers()</code>? Dynamic. Five rendering strategies, and you don't pick one - the framework infers it from your code. What could possibly go wrong.</p>
<p><strong>SolidStart</strong> is more straightforward:</p>
<ul>
<li><p><strong>SSR is on by default</strong> (<code>ssr: true</code>). Every route is server-rendered.</p>
</li>
<li><p><strong>SSG/prerendering</strong> is opt-in via config:</p>
</li>
</ul>
<pre><code class="language-ts">// app.config.ts
export default defineConfig({
  server: {
    prerender: {
      routes: ["/", "/about"],
      // or: crawlLinks: true
    },
  },
});
</code></pre>
<ul>
<li><p><strong>SPA mode</strong> if you want it: <code>ssr: false</code>.</p>
</li>
<li><p>Streaming SSR is supported out of the box - <code>&lt;Suspense&gt;</code> boundaries become streaming boundaries automatically.</p>
</li>
</ul>
<p>No ISR, no PPR, no implicit mode switching. You pick a mode and that's what you get. If you need per-route control, you handle it with server functions and caching primitives, not framework magic.</p>
<h3>Server-side code execution</h3>
<p>This is where the philosophies diverge most.</p>
<p><strong>Next.js</strong> has three main mechanisms for running code on the server:</p>
<ol>
<li><strong>Server Components</strong> (the default in App Router) - your component function runs on the server. It can <code>await</code> data, touch the filesystem, query databases. The rendered output streams to the client as RSC Flight payload. The client never sees the component's JavaScript.</li>
</ol>
<pre><code class="language-jsx">// This is a Server Component by default
async function ProductPage({ params }) {
  const { id } = await params;
  const product = await db.products.find(id);
  return &lt;ProductDisplay product={product} /&gt;;
}
</code></pre>
<ol>
<li><strong>Server Actions</strong> - functions marked with <code>"use server"</code> that handle mutations. Invoked from forms or client code, executed on the server.</li>
</ol>
<pre><code class="language-jsx">"use server";
export async function addToCart(formData) {
  const productId = formData.get("productId");
  await db.cart.add(productId);
  revalidatePath("/cart");
}
</code></pre>
<ol>
<li><strong>Route Handlers</strong> - traditional API endpoints in <code>app/api/*/route.ts</code> files.</li>
</ol>
<p>The boundary between server and client is managed by directives: <code>"use client"</code> marks a component as client-side, <code>"use server"</code> marks a function as server-only. The mental model of <em>what runs where</em> is the number one source of confusion in the App Router, per community surveys.</p>
<p><strong>SolidStart</strong> uses a single, consistent mechanism: <strong>server functions.</strong></p>
<p>Any function annotated with <code>"use server"</code> runs on the server. That's it. No Server Components, no implicit server/client boundary - you explicitly opt into server execution per function.</p>
<pre><code class="language-jsx">// A server function for reading data
const getProduct = query(async (id) =&gt; {
  "use server";
  return await db.products.find(id);
}, "product");

// A server function for mutations
const addToCart = action(async (formData) =&gt; {
  "use server";
  const productId = formData.get("productId");
  await db.cart.add(productId);
});
</code></pre>
<p>On the component side, you consume these with <code>createAsync</code>:</p>
<pre><code class="language-jsx">function ProductPage(props) {
  const product = createAsync(() =&gt; getProduct(props.params.id));

  return (
    &lt;Show when={product()}&gt;
      {(p) =&gt; &lt;ProductDisplay product={p()} /&gt;}
    &lt;/Show&gt;
  );
}
</code></pre>
<p>The difference in mental model is significant. If you've ever stared at a Next.js component wondering whether it runs on the server, the client, or both depending on the phase of the moon - yeah, that goes away. In SolidStart, all components run in the same context (SSR'd on the server, hydrated on the client) - you only think about which <em>functions</em> run on the server. One boundary to manage instead of two.</p>
<h3>Data fetching</h3>
<p><strong>Next.js:</strong></p>
<pre><code class="language-jsx">// Server Component - just await (server only)
async function Posts() {
  const posts = await db.posts.findMany();
  return &lt;PostList posts={posts} /&gt;;
}

// With caching (v16+)
"use cache";
async function CachedPosts() {
  const posts = await db.posts.findMany();
  return &lt;PostList posts={posts} /&gt;;
}

// Client-side - use SWR or TanStack Query
"use client";
function LivePosts() {
  const { data } = useSWR("/api/posts", fetcher);
  return &lt;PostList posts={data} /&gt;;
}
</code></pre>
<p>Next.js has gone through three caching models in three major versions. v14 cached <code>fetch()</code> calls aggressively by default (which confused everyone). v15 removed default caching. v16 introduced <code>"use cache"</code> as an explicit opt-in. On top of that, there are four distinct cache layers: Request Memoization, Data Cache, Full Route Cache, and Router Cache. If you've ever felt like you needed a PhD in cache invalidation just to fetch a list of blog posts, you're not alone.</p>
<p><strong>SolidStart:</strong></p>
<pre><code class="language-jsx">// Define a cached query with a key
const getPosts = query(async () =&gt; {
  "use server";
  return await db.posts.findMany();
}, "posts");

// Preload in route config
export const route = {
  preload: () =&gt; getPosts(),
};

// Consume in component
function Posts() {
  const posts = createAsync(() =&gt; getPosts());

  return (
    &lt;Suspense fallback={&lt;Loading /&gt;}&gt;
      &lt;For each={posts()}&gt;
        {(post) =&gt; &lt;PostCard post={post} /&gt;}
      &lt;/For&gt;
    &lt;/Suspense&gt;
  );
}
</code></pre>
<p>One caching primitive (<code>query</code>), one consumption primitive (<code>createAsync</code>), explicit cache keys, and <code>&lt;Suspense&gt;</code> for loading states. Mutations go through <code>action()</code>. Cache invalidation targets specific keys. That's the whole model.</p>
<h4>Cache invalidation, cookies, and revalidation</h4>
<p>Three things you'll need almost immediately after setting up data fetching:</p>
<p><strong>Invalidating cache from an action:</strong></p>
<p>When a mutation succeeds, you want to bust the relevant cache. In Next.js, you call <code>revalidatePath()</code> or <code>revalidateTag()</code>. In SolidStart, you use <code>revalidate()</code> with the cache key:</p>
<pre><code class="language-jsx">import { revalidate, action } from "@solidjs/router";

const addPost = action(async (formData) =&gt; {
  "use server";
  await db.posts.create({
    title: formData.get("title"),
    body: formData.get("body"),
  });
  revalidate("posts"); // bust the "posts" cache key
});
</code></pre>
<p>The cache key is the second argument you passed to <code>query()</code>. This is why explicit cache keys matter - you can surgically invalidate exactly what changed.</p>
<p><strong>Cookies and cached queries:</strong></p>
<p>In Next.js, reading <code>cookies()</code> or <code>headers()</code> inside a cached function implicitly makes it dynamic - the cache key includes the cookie values, but this behavior is invisible and easy to get wrong.</p>
<p>In SolidStart, if your server function reads cookies (via the request event), you need to be aware that <code>query()</code> caches by arguments only. The cookie value isn't automatically part of the cache key. If different users should see different data, pass a user identifier as an argument to the query, or use <code>revalidate()</code> on auth state changes:</p>
<pre><code class="language-jsx">const getUserData = query(async (userId) =&gt; {
  "use server";
  const session = getSession(); // read from cookie
  if (!session) throw redirect("/login");
  return await db.users.find(userId);
}, "userData");
</code></pre>
<p><strong>Revalidation timing:</strong></p>
<p>Next.js has ISR with <code>revalidate: 60</code> for time-based cache expiry. SolidStart doesn't have a built-in time-based revalidation primitive. Your options:</p>
<ul>
<li><p><strong>Explicit invalidation</strong> via <code>revalidate()</code> in actions (the recommended approach)</p>
</li>
<li><p><code>revalidate()</code> on the client to force a refresh of specific cache keys</p>
</li>
<li><p><strong>HTTP cache headers</strong> on your server functions if you want CDN-level caching:</p>
</li>
</ul>
<pre><code class="language-jsx">const getPosts = query(async () =&gt; {
  "use server";
  const event = getRequestEvent();
  event.response.headers.set("Cache-Control", "s-maxage=60, stale-while-revalidate");
  return await db.posts.findMany();
}, "posts");
</code></pre>
<p>This is more explicit than ISR, but it's also more predictable - you know exactly what's cached and for how long.</p>
<h3>API endpoints</h3>
<p><strong>Next.js</strong> uses Route Handlers:</p>
<pre><code class="language-ts">// app/api/posts/route.ts
export async function GET(request: Request) {
  const posts = await db.posts.findMany();
  return Response.json(posts);
}

export async function POST(request: Request) {
  const body = await request.json();
  const post = await db.posts.create(body);
  return Response.json(post, { status: 201 });
}
</code></pre>
<p><strong>SolidStart</strong> uses API routes in the same <code>routes/</code> directory:</p>
<pre><code class="language-ts">// routes/api/posts.ts
import { type APIEvent } from "@solidjs/start/server";

export async function GET(event: APIEvent) {
  const posts = await db.posts.findMany();
  return Response.json(posts);
}

export async function POST(event: APIEvent) {
  const body = await event.request.json();
  const post = await db.posts.create(body);
  return Response.json(post, { status: 201 });
}
</code></pre>
<p>Pretty similar on the surface, but there's an important difference hiding underneath. SolidStart uses the standard <code>Request</code>/<code>Response</code> Web APIs directly. Next.js <em>also</em> uses them in Route Handlers, but in practice you'll quickly run into <code>NextRequest</code> and <code>NextResponse</code> - extended wrappers that add things like <code>nextUrl</code>, <code>cookies()</code>, and <code>geo</code>. They're not standard, they don't exist outside Next.js, and they leak into your code in ways that make it non-portable. If you've ever tried to extract a Route Handler into a standalone function or test it without the Next.js runtime, you've felt this pain.</p>
<p>SolidStart gives you the event object with some extras (locals, request, response headers), but it's all built on Web APIs. The main difference is that SolidStart's API routes live alongside your page routes - no separate <code>api/</code> directory convention needed (though you can organize them that way if you want).</p>
<h3>Middleware</h3>
<p><strong>Next.js 16</strong> introduced <code>proxy.ts</code> as the recommended path for heavy server-side logic - auth, redirects, rewrites - running on the Node.js runtime. The old <code>middleware.ts</code> isn't fully deprecated, but it's now specialized for Edge Runtime only. For self-hosted setups (think Hetzner, Coolify, Docker), <code>proxy.ts</code> is what you want.</p>
<p><strong>SolidStart</strong> has <code>createMiddleware</code>:</p>
<pre><code class="language-ts">import { createMiddleware } from "@solidjs/start/middleware";

export default createMiddleware({
  onRequest: [authMiddleware, loggingMiddleware],
  onBeforeResponse: [metricsMiddleware],
});
</code></pre>
<p>One important caveat with SolidStart's middleware: <strong>it does NOT run during client-side navigation.</strong> Only on the initial server request. So don't use it as your sole auth check - put auth logic in your server functions too. Next.js has the same nuance with <code>proxy.ts</code>, but it's less obvious.</p>
<p><strong>What about</strong> <code>after()</code><strong>?</strong> Next.js 15 introduced <code>after()</code> - a way to schedule work (logging, analytics, cache warming) that runs after the response has been sent to the client. SolidStart doesn't have a direct equivalent. Your best options are firing off a non-awaited promise in your server function (the response won't wait for it), or using the <code>onBeforeResponse</code> middleware hook to queue background work. If you're on a platform that supports it (like Cloudflare Workers with <code>waitUntil()</code>), you can tap into that directly via the platform's API. It's less ergonomic than <code>after()</code>, but it works - and you're not locked into a framework-specific API.</p>
<p><strong>A note on auth in middleware:</strong> this is a broader anti-pattern that's worth calling out. Vercel spent years implicitly encouraging auth checks in Next.js middleware (their templates did it, their docs showed it), then pivoted to "we never said to do auth in middleware" when the Edge Runtime limitations became undeniable. The reality is that middleware auth is fine <em>if</em> it's asymmetric - checking a JWT signature or reading a session cookie without making additional network requests (no database lookups, no token introspection calls). The moment you need to hit an external service to validate, you're adding latency to every single request and creating a single point of failure. Put the real auth logic in your server functions where it belongs, and use middleware only for cheap, fast checks like redirecting unauthenticated users to a login page.</p>
<h3>Deployment</h3>
<p><strong>Next.js</strong> works best on Vercel (unsurprisingly). Self-hosting has improved - the Build Adapters API is now stable, and Node.js/Docker deployments are better supported than before. But some optimizations are still Vercel-specific, and Turbopack remains Next.js-only - not the general-purpose bundler it was initially marketed as.</p>
<p><strong>SolidStart</strong> deploys anywhere Nitro can target - and that's a long list: <strong>Node, Deno, Bun, Cloudflare (Workers, Pages), Netlify, Vercel, AWS Lambda, Deno Deploy</strong>, and more. Switch targets with a single config line:</p>
<pre><code class="language-ts">export default defineConfig({
  server: {
    preset: "cloudflare_pages", // or "netlify", "node", "deno", etc.
  },
});
</code></pre>
<p>No vendor lock-in. Vite is framework-agnostic with a massive ecosystem. This is the "primitives, not frameworks" philosophy applied to infrastructure.</p>
<h3>Metaframework cheat sheet</h3>
<table>
<thead>
<tr>
<th>Concern</th>
<th>Next.js 16</th>
<th>SolidStart</th>
</tr>
</thead>
<tbody><tr>
<td>Routing</td>
<td>File-based, special files (<code>page.tsx</code>, <code>layout.tsx</code>, <code>loading.tsx</code>)</td>
<td>File-based, file name = route, layouts via same-name convention</td>
</tr>
<tr>
<td>SSR</td>
<td>Default for Server Components, implicit mode switching</td>
<td>On by default, explicit config for SSG/SPA</td>
</tr>
<tr>
<td>Server code</td>
<td>Server Components + Server Actions + Route Handlers</td>
<td>Server functions (<code>"use server"</code>) - one mechanism</td>
</tr>
<tr>
<td>Data fetching</td>
<td><code>async</code> Server Components, <code>"use cache"</code>, SWR/TanStack for client</td>
<td><code>query()</code> + <code>createAsync()</code> - one model</td>
</tr>
<tr>
<td>Caching</td>
<td><code>"use cache"</code> + Cache Components (4 layers historically)</td>
<td><code>query()</code> with explicit cache keys</td>
</tr>
<tr>
<td>Mutations</td>
<td>Server Actions via <code>"use server"</code> in functions</td>
<td><code>action()</code> with <code>"use server"</code></td>
</tr>
<tr>
<td>API routes</td>
<td><code>app/api/*/route.ts</code></td>
<td><code>routes/api/*.ts</code></td>
</tr>
<tr>
<td>Middleware</td>
<td><code>proxy.ts</code> (Node) + <code>middleware.ts</code> (Edge)</td>
<td><code>createMiddleware</code> (server-only)</td>
</tr>
<tr>
<td>Self-hosting</td>
<td>Stable Adapter API</td>
<td>Nitro presets (native)</td>
</tr>
<tr>
<td>Optimization</td>
<td>React Compiler (auto-memoization)</td>
<td>No compiler needed (signals)</td>
</tr>
<tr>
<td>Bundler</td>
<td>Turbopack (Next.js-only)</td>
<td>Vite (framework-agnostic)</td>
</tr>
<tr>
<td>Deployment</td>
<td>Best on Vercel, Adapter API for others</td>
<td>Anywhere via Nitro presets</td>
</tr>
</tbody></table>
<hr />
<h2>Primitives, not frameworks</h2>
<p>Before we get into the "why bother" section, it's worth understanding the philosophy behind Solid. There's a mantra in the Solid community: <strong>"Primitives, not frameworks."</strong></p>
<p><em>A note on origins:</em> the phrase itself was actually coined by <a href="https://www.allthingsdistributed.com/">Werner Vogels</a>, AWS CTO, in the context of cloud infrastructure during a Re:Invent keynote. <a href="https://dev.to/ryansolid/5-ways-solidjs-differs-from-other-js-frameworks-1g63">Ryan Carniato</a> ran with it and applied it to UI frameworks, where it's taken on a meaning of its own. It's a bit of an appropriation - Vogels was talking about composable cloud services, not reactive UI primitives - but the core idea translates well: give developers building blocks, not opinionated monoliths.</p>
<p>The idea is simple: instead of giving you a monolithic framework with opinions about every layer of the stack, Solid gives you small, composable, reactive building blocks that you combine however you need. To paraphrase Ryan's approach: here are a few powerful concepts - learn them, combine them, build on top of them. That's the whole thing.</p>
<p>This shows up everywhere in the ecosystem:</p>
<ul>
<li><p><a href="https://primitives.solidjs.community/"><strong>Solid Primitives</strong></a> - a community library of composable building blocks organized by domain (inputs, media, browser APIs, network, animation). Each one is tree-shakeable, SSR-safe, and individually useful.</p>
</li>
<li><p><strong>SolidStart's architecture</strong> - v2 replaced its custom server layer (Vinxi) with Vite 6's Environment API, leveraging an existing ecosystem primitive rather than maintaining bespoke infrastructure.</p>
</li>
<li><p><strong>The plugin model</strong> - features like image optimization are opt-in plugins, not baked into the framework. You compose what you need.</p>
</li>
</ul>
<p>Contrast this with React's trajectory. <a href="https://react.dev/blog/2024/12/05/react-19">React 19</a> shipped with Actions, <code>useActionState</code>, <code>useOptimistic</code>, <code>use()</code>, Server Components, Server Actions. <a href="https://react.dev/blog/2025/10/01/react-19-2">React 19.2</a> added <code>&lt;Activity&gt;</code> (for hiding UI while preserving state and unmounting effects) and <code>useEffectEvent</code>. The <a href="https://react.dev/blog/2025/10/07/react-compiler-1">React Compiler hit v1.0</a>, auto-memoizing your code at build time. And that's before you add <a href="https://nextjs.org/blog/next-16">Next.js 16</a> on top with <code>"use cache"</code> directives, <code>proxy.ts</code> for server-side logic, Turbopack, and Build Adapters.</p>
<p>It's a lot. The React Compiler is genuinely impressive engineering - it makes React faster without changing the fundamental model. But in a sense, it's a patch on an architecture that other frameworks have moved past. Solid solves the performance problem at the architectural level via fine-grained reactivity, making a compiler unnecessary. <a href="https://blog.openreplay.com/reactivity-react-vue-angular-svelte/">Angular</a>, <a href="https://vuejs.org/guide/extras/reactivity-in-depth">Vue</a>, and <a href="https://svelte.dev/blog/runes">Svelte</a> have all moved toward signals too. React is the outlier choosing to solve it with tooling rather than primitives. (Though it's worth noting that React itself has <a href="https://react.dev/blog/2026/02/24/the-react-foundation">moved to an independent foundation</a> under the Linux Foundation - a governance change that may influence its future direction.)</p>
<p>Neither approach is "wrong." But they lead to very different developer experiences.</p>
<hr />
<h2>Why bother? Performance and DX</h2>
<p>So, that's a lot of "this works differently." Why go through the trouble?</p>
<h3>Bundle size</h3>
<p>Solid's core clocks in at <a href="https://bundlephobia.com/package/solid-js">~7KB min+gzip</a>. React + ReactDOM sits at <a href="https://bundlephobia.com/package/react-dom">~45KB min+gzip</a>. That's roughly a 6x difference at the framework level, and the gap compounds in real-world apps as you add routing, state management, and other dependencies. Less JavaScript shipped means faster load times. Simple math.</p>
<h3>No virtual DOM overhead</h3>
<p>React diffs a virtual tree on every state change, even with the Compiler auto-memoizing what it can. Solid compiles JSX to direct DOM operations. When a signal updates, only the specific DOM nodes that read that signal get touched. No diffing, no reconciliation, no wasted work. This is why Solid consistently ranks at or near the top of the <a href="https://krausest.github.io/js-framework-benchmark/">JS Framework Benchmark</a>, performing close to vanilla JavaScript.</p>
<h3>The DX angle</h3>
<p>Next.js has grown into a complex beast. Four layers of caching, three different caching models across three major versions (v14's aggressive implicit caching, v15's opt-out, v16's <code>"use cache"</code> opt-in), the server/client boundary dance, the middleware-to-proxy.ts migration… the <a href="https://2025.stateofjs.com/en-US/libraries/meta-frameworks/">State of JS 2025 survey</a> showed Next.js with the largest satisfaction drop of any meta-framework (from 68% to 55%), landing as both the 13th most-loved and 5th most-hated project - uniquely polarizing. A commonly cited complaint? <em>"Too complex."</em> (paraphrased)</p>
<p>And then there was <a href="https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components">React2Shell</a> (CVE-2025-55182) - a CVSS 10.0 critical RCE vulnerability in the React Server Components Flight protocol. A single malicious POST request to any server function endpoint could execute arbitrary code. Default <code>create-next-app</code> deployments were vulnerable out of the box. It was <a href="https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components">patched across all React 19.x lines</a> (19.0.1, 19.1.2, and 19.2.1 - make sure you're on the right patch for your minor version), and Next.js shipped corresponding fixes. But the architectural debate about the <a href="https://www.wiz.io/blog/critical-vulnerability-in-react-cve-2025-55182">Flight protocol</a> - and the attack surface of running component deserialization on the server - remains a valid concern when evaluating RSC-heavy architectures.</p>
<p>SolidStart, by contrast, is refreshingly lean. File-based routing, <code>"use server"</code> directives for server functions, <code>query()</code> for cached data fetching, <code>action()</code> for mutations, and Vite under the hood. It leans on the web platform and doesn't try to reinvent every wheel. You write the reactive primitives we covered above, and the metaframework gets out of your way.</p>
<p>That said - SolidStart's ecosystem is smaller. You won't find the same breadth of third-party integrations, and the community, while active and growing (<a href="https://github.com/solidjs/solid">35K+ GitHub stars</a>, <a href="https://www.npmjs.com/package/solid-js">~1.5M weekly npm downloads</a>), is not React-sized. It's a tradeoff worth being honest about - however, in my personal experience, that hasn't been a big deal. I've already shipped 5+ Solid-based projects of various complexity levels. I can't say the ecosystem size was a huge problem.</p>
<hr />
<h2>Can LLMs help with the migration?</h2>
<p>Short answer: yes, but trust and verify.</p>
<p>LLMs like Claude, ChatGPT, and tools like Cursor are genuinely useful for the mechanical parts of migration. They'll convert <code>useState</code> to <code>createSignal</code>, swap <code>className</code> for <code>class</code>, strip out <code>React.memo</code> and <code>useCallback</code> (neither are needed in Solid), and handle the basic boilerplate. For a large codebase, that saves real time.</p>
<p>But here's the catch: LLMs are trained on <em>way</em> more React code than Solid code. Their muscle memory defaults to React patterns, and the places where they fail are exactly the places where Solid differs most:</p>
<ol>
<li><p><strong>Props destructuring</strong> - the #1 failure. Every LLM will write <code>({ name, onClick })</code> by default. In Solid, this kills reactivity. You need <code>props.name</code> or <code>splitProps()</code>.</p>
</li>
<li><p><strong>Control flow</strong> - LLMs leave <code>.map()</code>, ternaries, and <code>&amp;&amp;</code> in JSX instead of converting to <code>&lt;For&gt;</code>, <code>&lt;Show&gt;</code>, and <code>&lt;Switch&gt;</code>/<code>&lt;Match&gt;</code>. The JS patterns "work" but defeat Solid's fine-grained updates.</p>
</li>
<li><p><strong>The "runs once" model</strong> - LLMs generate code that assumes the component function re-runs on state changes. It doesn't in Solid.</p>
</li>
<li><p><strong>Async in effects</strong> - LLMs will happily write <code>createEffect(async () =&gt; {...})</code>, which breaks reactive tracking after the first <code>await</code>.</p>
</li>
</ol>
<p>There's a <a href="https://github.com/solidjs/solid/discussions/1866">documented case</a> of ChatGPT confidently telling a user that a bug in their <code>&lt;Show&gt;</code> usage was a Solid framework bug. It wasn't. The LLM just didn't understand Solid's reactivity model.</p>
<h3>Making it work</h3>
<p>The good news is you can steer LLMs in the right direction:</p>
<ul>
<li><p><strong>SolidJS now ships an</strong> <a href="https://docs.solidjs.com/llms.txt"><strong>official llms.txt</strong></a> that you can feed to your AI tool as context. If you're using Cursor, add it via <code>@Docs</code>.</p>
</li>
<li><p><a href="https://github.com/ysdede/solidjs-context-llms"><strong>solidjs-context-llms</strong></a> is a community-maintained, LLM-optimized documentation suite organized by domain (reactivity, routing, SSR, primitives).</p>
</li>
<li><p><strong>Cursor users</strong> can grab SolidJS-specific <a href="https://github.com/PatrickJS/awesome-cursorrules">.cursorrules files</a> that enforce the right patterns.</p>
</li>
<li><p><strong>Claude Code users</strong> can add Solid rules to their <code>CLAUDE.md</code> to prevent the common mistakes.</p>
</li>
</ul>
<p>But the real safety net is <a href="https://github.com/solidjs-community/eslint-plugin-solid"><strong>eslint-plugin-solid</strong></a>. Run it after every AI-assisted conversion. It catches reactivity violations that LLMs introduce: destructured props, conditional logic outside JSX, incorrect imports. Think of it as the linter that keeps the AI honest.</p>
<p>My recommended workflow: convert one component at a time, review the output for the known failure patterns, and run the linter before moving on. LLMs get you 70-80% of the way there. The last 20% is where understanding the mental model matters, and that's something you have to bring yourself.</p>
<hr />
<h2>A note on SolidStart v2</h2>
<p>If you're evaluating SolidStart right now, you should know where things stand. <a href="https://github.com/solidjs/solid-start/discussions/2119">SolidStart v2 is in late alpha</a> and already being deployed in production by early adopters. The big change: it replaces Vinxi (v1's custom server layer) with <a href="https://github.com/solidjs/solid-start/discussions/1743">Vite's Environment API</a>, giving you a leaner, more debuggable stack that leverages Vite's existing ecosystem. <a href="https://github.com/solidjs/solid-start/discussions/1960">Nitro 3 integration</a> is a separate effort, still in progress.</p>
<p>The migration from v1 to v2 is minimal - mostly config changes. Your component code and server functions stay the same. Everything in this guide covers the fundamentals that apply to both versions.</p>
<hr />
<h2>Where to go from here</h2>
<p>If you want to get your hands dirty:</p>
<ul>
<li><p><a href="https://docs.solidjs.com">SolidJS docs</a> - genuinely well-written</p>
</li>
<li><p><a href="https://www.solidjs.com/tutorial/introduction_basics">SolidJS Tutorial</a> - interactive, takes about an hour</p>
</li>
<li><p><a href="https://docs.solidjs.com/solid-start">SolidStart docs</a> - for when you're ready to build an app</p>
</li>
<li><p><a href="https://primitives.solidjs.community/">Solid Primitives</a> - community library of composable building blocks</p>
</li>
<li><p><a href="https://github.com/solidjs/solid-start/discussions/2119">SolidStart v2 status</a> - late alpha, heading toward beta, already deployed in production by early adopters</p>
</li>
</ul>
<hr />
<h2>Takeaway</h2>
<p>Migrating from React to Solid isn't a find-and-replace job. The JSX similarity is a double-edged sword - it makes the syntax feel familiar while hiding a fundamentally different execution model. But once the "runs once" mental model clicks, you'll find that Solid's approach is simpler and more predictable. And on a personal note, it makes my brain hurt less.</p>
<p>We're also witnessing a convergence on fine-grained reactivity as the right model - <a href="https://blog.openreplay.com/reactivity-react-vue-angular-svelte/">Angular adopted signals</a>, <a href="https://vuejs.org/guide/extras/reactivity-in-depth">Vue has <code>ref()</code> and <code>reactive()</code></a>, <a href="https://svelte.dev/blog/runes">Svelte 5 moved to runes</a>. Solid has been doing this from day one. The primitives are mature, the metaframework is production-ready, and the philosophy of composable building blocks over monolithic abstractions means you're not fighting the framework - you're using it as intended.</p>
<p>Whether you're moving away from Next.js for practical reasons, performance reasons, or just because you're curious about what fine-grained reactivity feels like in practice - give SolidStart a shot. The learning curve is real but short, and the payoff is worth it.</p>
<p>Happy hacking!</p>
<hr />
<p><em>Originally published at</em> <a href="https://darko.io/posts/next-to-solidstart-migration-guide/"><em>darko.io</em></a> <em>by Darko Bozhinovski. Licensed under</em> <a href="https://creativecommons.org/licenses/by/2.0/"><em>CC BY 2.0</em></a><em>.</em></p>
]]></content:encoded></item><item><title><![CDATA[Software engineering enters its accounting era]]></title><description><![CDATA[If you have ever done accounting, you probably know it is not much fun. If not here is a brief summary. There are bunch of rules to follow, coded into local, national, and international standards, and]]></description><link>https://blog.42.mk/software-engineering-enters-its-accounting-era</link><guid isPermaLink="true">https://blog.42.mk/software-engineering-enters-its-accounting-era</guid><dc:creator><![CDATA[Pavlina Buchevska]]></dc:creator><pubDate>Wed, 01 Apr 2026 12:14:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/44a8b892-d99e-47c9-a0f8-e364e5c721df.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you have ever done accounting, you probably know it is not much fun. If not here is a brief summary. There are bunch of rules to follow, coded into local, national, and international standards, and many modern ERP systems have them at least partially implemented and automated. There is not really creativity in the work – well not unless you want to work in organised crime, and the profession is highly regulated. There is a lot of gate keeping about who can do the job, or who can do which part of the job, and what kind of certificates one must have, although the core of the accounting process is largely unchanged since it was codified sometime during the <a href="https://en.wikipedia.org/wiki/Luca_Pacioli">renaissance or thereabouts</a>.</p>
<p>No one has accounting as a hobby. There is no cool project in accounting you can share with your friends, and there aren’t any interesting or thought provoking conferences or meetups where the topic is accounting. Was there ever an accountant speaking at TED? I haven’t check, but if there was he/she was probably speaking about something else.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/929c9a92-617b-4da8-b23f-30e56395ab40.png" alt="" style="display:block;margin:0 auto" />

<p>So what has this have to do with software engineering? To use Don McMillan’s funny diagram, we are now seeing a shift away from problem solving in software engineering. Instead of solving problems we are writing markdown documents that tell agents to solve a problem. And, even in these early days we see projects popping up with default <code>.md</code> files to do a certain tasks, or write in certain language. I think it is not before long we see a standard <code>.md</code> file to build an R package, or to build a FastAPI, or something else, with complexity of the problem being solved increasing over time.</p>
<p>There is a trajectory in which agent skill will become something akin to International Financial Reporting Standards. I think we are not far away from having the big companies producing “verified” skill (as I went to LinkedIn to post this I was greeted by a <a href="https://www.linkedin.com/posts/antonabyzov_ai-developertools-buildinpublic-share-7438679148476125184-8lnk">post by Anton Abyzov</a> that talks about a <a href="https://verified-skill.com/">verified skill tool</a> :) to do certain things in their ecosystem. Think for example “AWS verified skills for infrastructure”, and maybe these will start to come with a price tag on a tier level, or even on regional level around the world.</p>
<p>Then if you are a company working in software auditing, you will probably have a skill that will instruct your agent to audit the work of another agent. I would not be surprised if the Big 4 are already thinking about this.</p>
<p>Where does this leave software engineers? No more problem solving, more OCD? I think it is not difficult to imagine future work being mostly reading <code>.md</code> files and trying to catch inconsistencies that slip through spell checkers or similar tools. Think someone typing <code>cat</code> instead of <code>car</code> in some <code>.md</code> file, and someone else trying to figure out where is the error coming from. That to me sounds much closer to tracking the stray balance mismatch on the balance sheet, than to improving a poorly implemented function that sometimes crashes the user’s computer.</p>
<p>Is this good or bad? I guess it depends on being good or bad for whom or what. I think that even if this trajectory takes place, it will probably not affect the current generation of software engineers.</p>
<p>However, not thinking about the other issues being raised (such as overall environmental concerns, employment outlook, and the stock market bubble), I think over time we are very likely to witness <a href="https://en.wikipedia.org/wiki/Tragedy_of_the_commons">tragedy of the commons</a> unraveling in the free software world.</p>
<p>Then maybe a lot of the software becomes like the cheap umbrellas you buy on the street form ad-hoc sellers when it suddenly starts to rain, and then they break after the second use and you toss them away. And maybe a lot of the software becomes a problem similar to the plastic in the oceans and <a href="https://www.theguardian.com/world/2026/mar/15/cairo-fishers-catching-plastic-bottles">some engineers re-skill to deal with that</a>.</p>
<p>For the current generation I think it will put pressure on community building around software, hobby projects, and events. Think about it: why would I want to come to a meetup if we are to discuss your latest <code>.md</code> file? It is not meant for people anyway. Or why would I bother with reporting a bug or offering a pull request on a project that is largely build by a coding agent? The intrinsic motivation to help and be involved with people is simply gone. I would even say there is no motivation to send my agent subscription to do a pull request on your agent driven project. What would my lighting talk be at the next conference: how to format your <code>.md</code> file in 10 easy steps? There is a skill for that too.</p>
<p>After all have you ever heard of an accounting community?</p>
]]></content:encoded></item><item><title><![CDATA[Just put the QR in the bag bro]]></title><description><![CDATA[TLDR: Frustrated by BS hostage-happy websites for generating QR codes, I built/vibed https://qr.42.mk in an hour or so, go ahead, use it, and whenever you need a QR code, just go there, grab it and mo]]></description><link>https://blog.42.mk/just-put-the-qr-in-the-bag-bro</link><guid isPermaLink="true">https://blog.42.mk/just-put-the-qr-in-the-bag-bro</guid><dc:creator><![CDATA[Ilija Boshkov]]></dc:creator><pubDate>Tue, 24 Mar 2026 11:27:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/597da4af-b11f-494d-a24e-0ac5a6fa9af5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TLDR:</strong> Frustrated by BS hostage-happy websites for generating QR codes, I built/vibed <a href="https://qr.42.mk">https://qr.42.mk</a> in an hour or so, go ahead, use it, and whenever you need a QR code, just go there, grab it and move on with your life.</p>
<p>Now, while the tool was 100% AI generated with very minor tweaks from my side, this post was 99% human written, 1% being the titles for more manageable reading, I'm by no means a good writer, if you want to read the story, thank you, and go right ahead!</p>
<p>You can find me on the Base42 Discord if you want to chat about the blog, the universe or anything.</p>
<h2>Step 1: You Just Need a QR Code</h2>
<p>Picture this - you're building promotional materials for your event, venue or whatever, you have your design ready to go, a placeholder in the middle for a QR code, your guests will scan it and learn more about your event, it will be great!</p>
<p>You, being the well-prepared designer that you are, prepare your media materials months ahead, painstakingly designing and detailing everything, "It's perfect!" you exclaim, then you remember that you still have a placeholder in your QR code slot, so you do what anyone would do:</p>
<ol>
<li><p>Type in QR code generator into <a href="http://google.com">google.com</a></p>
</li>
<li><p>Paste your link</p>
</li>
<li><p>A popup comes up prompting you to sign in</p>
<ol>
<li><p>It has Login with Google available, so why not</p>
</li>
<li><p>You login</p>
</li>
<li><p>You click free plan or something</p>
</li>
</ol>
</li>
<li><p>Download the QR code</p>
</li>
<li><p>You get your QR Code</p>
</li>
<li><p>???</p>
</li>
<li><p>Profit</p>
</li>
</ol>
<p>Victory! Now that whole side quest is complete, you put the cherry on top, your QR code is finally placed into the placeholder, the whole picture finally tied together, feng shui in place and all that. Being the diligent designer that you are, you test it and validate it works, huzzah!</p>
<p>Ship it! You send it to the print company, they print it and a few days later, you have your beautiful designs ready to be passed around as fliers, stickers or whatever other physical media may be. A week or so later you start your campaign and the flyers get passed around, people are scanning, conversions are happening, yay!</p>
<h2>The Hostage Email</h2>
<p>Then a few days later, you get this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/5f7f2ceeaddab004867df3f3/e16720f3-f4d3-4bf0-a8ac-c54d4f4a1841.png" alt="" style="display:block;margin:0 auto" />

<p>Oh yeah, that QR code you generated, plopped into your design, shipped to printing and paid the invoice for - it's being held hostage by one of the many predatory QR code generators out there, all of which seem to rank pretty high on Google. I could write an entire blog post on the predatory tactics and dark patterns they use to cling onto any desperate attempt at monetizing something that is at best a single use app. Sure, they provide tracking and analytics, but that's not what you need every time, and it should certainly not be the default.</p>
<p>To continue, pay up, friend:</p>
<img src="https://cdn.hashnode.com/uploads/covers/5f7f2ceeaddab004867df3f3/ebf9a30c-f75c-43b5-845e-066231aa22d4.png" alt="" style="display:block;margin:0 auto" />

<p>Just $9.99/month, what a steal!</p>
<h2>I'll Build My Own, With Clankers and Prompting</h2>
<p>If you've been following Base42, you know what we are - a hackerspace, where people hack on different projects, experiment and so on. If you're following us on socials, are in the Base42 Discord server, or just maybe stumbled upon <a href="https://blog.42.mk/everything-remote-as-all-things-should-be">Vasilaki's last post</a> you would know that last month we held NSND, a sort of un-conference, where you just come, talk about random stuff and maybe something comes out of it.</p>
<p>So we did. Someone mentioned generating a QR code and immediately triggered flashbacks of every time I've fallen victim to this exact hustle.</p>
<h2>One Wish, One Hour</h2>
<p>There's this magic black box called AI that you can throw your wishes into, something clanks along and voila, you have your very own app, vibes and all. That's what I did - while everything else was happening during NSND, people showing off home infrastructure setups, talking about what we'd host on the new Base42 server rack and so on, I put my wish into the wishing well that is a coding agent.</p>
<p>Here's what my wish was:</p>
<blockquote>
<p>"Build a QR code generator, should work fully in the browser, make it available as a PWA, no server, make no mistakes"</p>
</blockquote>
<p>And it did. Very little mistakes, pretty much a 1-shot from the get-go, and around 1h in it was fully deployed.</p>
<h2>Meet QR Canvas</h2>
<p>I present to you QRC - let's call it QR Canvas, the pronunciation makes it funny.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5f7f2ceeaddab004867df3f3/ddff9c8a-0cb5-47b2-b17e-abfdc43719b8.png" alt="" style="display:block;margin:0 auto" />

<p>It supports everything you would need and more - fully customize your QR code, add a logo, pick from templates, all for free, all within your browser. You can even install the PWA on your phone, tablet, laptop and take it with you offline.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5f7f2ceeaddab004867df3f3/28913e15-fd24-4b56-b3cb-20d475760d9c.png" alt="" style="display:block;margin:0 auto" />

<p>There's even support for adding a logo!</p>
<img src="https://cdn.hashnode.com/uploads/covers/5f7f2ceeaddab004867df3f3/341d859e-dbc6-479a-874a-15bb45e48a06.png" alt="" style="display:block;margin:0 auto" />

<h2>Goes without saying, it's Open Source</h2>
<p>The code is all there if you want to poke around, contribute, or just verify that yes, it really is just a browser and nothing else. Fork it, improve it, make it yours. Heed my warning though, the codebase IS AI SLOP, essentially, but it's AI slop that totally works, does the job and doesn't need further updates as far as I'm concerned. If you have a better coding agent that would like to rewrite this in the JS framework dejure, go ahead and open a PR, I'll tell my AI to check your AI's work! 🤣</p>
<p><a href="https://github.com/42dotmk/qrc">github.com/42dotmk/qrc</a></p>
<h2>Go Use It</h2>
<p>No account. No popup. No hostage email three weeks from now. Just <a href="https://qr.42.mk">qr.42.mk</a> - open it, generate your code, download it, done.</p>
<p>If you find a bug, fix it yourself.</p>
]]></content:encoded></item><item><title><![CDATA[Everything remote, as all things should be]]></title><description><![CDATA[In the spirit of the hacker culture of Base42, we held the unconference NSND(Ništa se neće dogoditi).
Macedonian NSND has it's own wiki, where you can learn more about what it is and what has happened]]></description><link>https://blog.42.mk/everything-remote-as-all-things-should-be</link><guid isPermaLink="true">https://blog.42.mk/everything-remote-as-all-things-should-be</guid><dc:creator><![CDATA[Vasilaki Totsili]]></dc:creator><pubDate>Wed, 18 Mar 2026 13:08:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/789459e8-bade-41ad-978a-0f8a1eae7b67.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the spirit of the hacker culture of Base42, we held the <a href="https://en.wikipedia.org/wiki/Unconference">unconference</a> <strong>NSND</strong>(<em>Ništa se neće dogoditi</em>).</p>
<p>Macedonian NSND has it's own <a href="https://wiki.spodeli.org/%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%98%D0%B0:%D0%9D%D0%A1%D0%9D%D0%94">wiki</a>, where you can learn more about what it is and what has happened over the years in each city it was held in(reading back it seems pretty fun and I am sure there were crazy adventures).</p>
<p>Shortly, it's a gathering of tech enthusiasts and geeks from the Balkan region, but sometimes there are guests from abroad, there aren't predefined/predetermined topics nor speakers, everything happens on the spot. People come, introduce themselves, say what they can speak and listen to about and after a vote, the talks/workshops start.</p>
<p>It's like an organized chaos, breeze of fresh air for the people that are used to structured talks and events.</p>
<hr />
<h3>Skopje 2026 NSND</h3>
<p>We voted for the topics we proposed at the start of the event and the second most anticipated topic was mine:</p>
<h2>Decrypt LUKS through SSH</h2>
<h3><em>SSHing into the machine before the system is booted to decrypt the LUKS-encrypted drive</em></h3>
<p>When the drive is decrypted, the drive boots and runs the installed operating system into which we can practice the normal everyday SSHing(remotely accessing).</p>
<hr />
<h2>Motive</h2>
<p>We started working on a 3D printer server @ Base42(more on that in some other upcoming blog), but stumbled upon a challenge. Few times, in the course of three days, the server was unplugged from the power outlet and couldn't be accessed remotely via SSH even if immediately plugged in, soooo annoying. You may ask why? - LUKS is the answer.</p>
<p>If you want your entire disk to be encrypted for security and privacy, on Linux, that's done using <a href="https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup">Linux Unified Key Setup(LUKS)</a>. You must be in front of your computer to unlock LUKS-protected disks by providing a passphrase at boot time. However, the machine I was working with was not at my premise, so I wasn’t able to unlock and boot it, at least that's what I thought at first.</p>
<p>The old workflow went like this:</p>
<ol>
<li><p>server was working properly(normal SSH would work) → someone unplugs the cable(SSH timing out on my machine)</p>
</li>
<li><p>I would need to come physically → decrypt the LUKS</p>
</li>
<li><p>system boots, SSH works again → go to step 1</p>
</li>
</ol>
<p>Because I was furious about it, I said to myself-"There must be a way to remotely do this. I am sure the DevOps of big companies are not coming physically to the encrypted servers everytime they restart them just to decrypt them", so I searched the internet for my thought, and there it was again, someone else did it, the usual.</p>
<p>There are always exceptions(sad), as in our case, if the cable is left unplugged, I MUST come physically to the server.</p>
<p>PLEASE DON'T LEAVE THE CABLES UNPLUGGED IN BASE42</p>
<hr />
<h2>Execution</h2>
<p>When I read that there's such thing as remote unlocking a LUKS encrypted drive, as every hobby project, I said-"I am going to do this one day" and it just waited for the right moment.</p>
<p>The perfect moment was NSND.</p>
<p>But I wasn't prepared for it. I just proposed the topic, it was voted, but I didn't know how to do it exactly.</p>
<p>Luckily, that's what the unconference was about-everyone was involved into it and wanted to help to do it. After seeing that it will take a lot of time, more than a quick talk, we decided to make it like a separate workshop, projected to the secondary projector @ Base42.</p>
<p>The biggest credit goes to Damjan Georgievski.</p>
<p>And here's what we learned from the process:</p>
<h3>Choosing the SSH server</h3>
<p>TinySSH and Dropbear are both lightweight SSH servers designed for embedded systems and minimalist environments, but they prioritize different goals.</p>
<p><strong>Dropbear is more feature-rich</strong> and compatible, supporting password authentication, older algorithms, and scp/sftp, making it a better general-purpose <a href="https://www.openssh.org/"><strong>OpenSSH</strong></a> replacement and is perfect for routers, IoT, and embedded Linux with limited resources.</p>
<p><strong>TinySSH focuses on extreme security/simplicity</strong> by supporting only modern, secure cryptography (Ed25519/ECDSA, no RSA/DSA), has extremely small codebase aimed at minimizing attack surface and is ideal for highly secure, modern <strong>initramfs</strong> unlocking.</p>
<p><strong>TinySSH</strong> looked like the exact tool we need.</p>
<h3>initramfs</h3>
<p>What if I told you that there's a full second operating system(although very small) that runs before your known main OS, <em>I am using Arch, btw</em>.</p>
<p>Well, that happens in the early boot of a Linux machine, where the only directory that's not encrypted by default is the <em>/boot/.</em></p>
<p>This secondary OS is not accessing the LUKS-encrypted drive/s, is loaded from a compressed archive file in <em>/boot/</em> called <code>initrd</code>, which stands for “initramdisk”, which is another word for our <em>initramfs</em> system and it contains all the boot-related things for the system, of which most important for us to have are the <strong>drivers</strong> and the <strong>ssh server</strong>.</p>
<p>It is ran from <strong>RAM</strong>, that's why it's called <strong>initramfs</strong> (initial RAM filesystem).</p>
<p>In it, a copy of <em>systemd</em> is running and its state is passed to the state in the primary OS, so that means we can install a whole collection of stuff in <code>mkinitcpio-systemd-extras</code> (<code>mkinitcpio</code> is the tool Arch uses to regenerate <strong>initramfs</strong>).</p>
<p>Here's a link to see what's available: <a href="https://github.com/wolegis/mkinitcpio-systemd-extras">https://github.com/wolegis/mkinitcpio-systemd-extras</a>. Since it's AUR, use your prefered way to add them to your Arch packages.</p>
<p>Of those, we need two: <a href="https://github.com/wolegis/mkinitcpio-systemd-extras/blob/main/sd-tinyssh">sd-tinyssh</a> and <a href="https://github.com/wolegis/mkinitcpio-systemd-extras/blob/main/sd-network">sd-network</a>. They are added in the <code>/etc/mkinitcpio.conf</code>'s <code>HOOKS</code>, where its comment reads:</p>
<blockquote>
<p>The HOOKS control the modules and scripts added to the image, and what happens at boot time.</p>
<p>Order is important, and it is recommended that you do not change the</p>
<p>order in which HOOKS are added.</p>
</blockquote>
<p>Here's our <code>HOOKS</code>:</p>
<pre><code class="language-shell">HOOKS=(base systemd autodetect microcode modconf kms keyboard keymap sd-vconsole block mdadm_udev sd-network sd-resolve sd-tinyssh sd-encrypt filesystems fsck)
</code></pre>
<p>To ensure the tinySSH server only decrypts the LUKS, we have created the file <code>/etc/mkinitcpio.conf.d/tinyssh.conf</code> with the content:</p>
<pre><code class="language-shell">SD_TINYSSH_COMMAND="systemd-tty-ask-password-agent --query --watch"
</code></pre>
<p>For the tinySSH to work, we need the public keys into <code>/root/.ssh/authorized_keys</code>.</p>
<p>In our case, I also needed to edit <code>/etc/systemd/network/en.network</code> to put the leased static internal IP address via DHCP Reservation, because when we used the default <code>DHCP=yes</code>, the tinySSH server was given a different IP address every time and wasn't accessible:</p>
<pre><code class="language-shell">[Match]
Name=en* eth*

[Network]
Address=192.168.x.x/24
Gateway=192.168.x.1
DNS=8.8.8.8
</code></pre>
<p>Finally, run <code>mkinitcpio -P</code> to rebuild the <em>initramfs</em>.</p>
<hr />
<h2>Result</h2>
<img src="https://cdn.hashnode.com/uploads/covers/69a8321ae55311e40ff87fce/8df5581e-a264-4919-9136-82b44fa97c79.gif" alt="" style="display:block;margin:0 auto" />

<blockquote>
<p>Disclaimer: I am using alias commands( <code>printon</code>/ <code>printonLuks</code>), you would need to use your own parameters e.g. <code>ssh -p {server_ssh_port} -i "{path_to_private_ssh_key}" {user}@{server_ip_address}</code></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[PxBadge - Dynamically generated Pixel Art Badges
]]></title><description><![CDATA[As both Developer and Pixel Artist, I often find myself creating pixel art for my projects, be it placeholders, icons or even as larger parts of some application or game.My latest such project came fr]]></description><link>https://blog.42.mk/pxbadge-dynamically-generated-pixel-art-badges</link><guid isPermaLink="true">https://blog.42.mk/pxbadge-dynamically-generated-pixel-art-badges</guid><dc:creator><![CDATA[Pavlina Buchevska]]></dc:creator><pubDate>Wed, 04 Mar 2026 11:54:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/d130606c-edc1-489d-8666-a060819c0e7a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As both Developer and Pixel Artist, I often find myself creating pixel art for my projects, be it placeholders, icons or even as larger parts of some application or game.<br />My latest such project came from the wish to showcase technologies I'm fond of on my GitHub profile. Instead of looking for specific technology logos, then manually editing the images, I wanted to have badges with a consistent style, that can be dynamically generated and tweaked in the future for different needs, as well as a way for anyone to create their own badges.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/52cc5766-78b6-40c2-823c-7875a92d7c36.png" alt="" style="display:block;margin:0 auto" />

<p>From looking around, I was unable to find any existing pixel art online for the majority of the techs I needed, and I hope with this project, future adventurers don't run into the same issues as me 😊</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/790ae817-9cc7-4d1d-9063-c6980d5baac5.png" alt="" style="display:block;margin:0 auto" />

<p>I drew pixel art of a few logos, made a custom badge and stars, and created a Python script that generates a badge with the desired logo and the specified rating underneath. I created a web API so anyone can make their own badges for whatever they desire, profile, portfolio, home page, game project, etc.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/26da7734-9d13-4b14-8250-d6d7c273ee42.png" alt="" style="display:block;margin:0 auto" />

<p>With all the different badge material, technology and rating options, there are <strong>over 4000</strong> possible badge combinations. If we include the different scale options, this number jumps to <strong>over 80,000</strong>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/5529cc8e-2822-4854-9d8a-0673baec3c54.png" alt="" style="display:block;margin:0 auto" />

<h2>Implementation</h2>
<p>The project implementation is quite simple and consists of a Vanilla JS frontend (as there is nothing fancy going on there), and a FastAPI backend.<br />The backend loads all the sprites from the assets folder initially and from then onwards they are stored in-memory, with an additional (protected) endpoint existing if I need to reload the loaded files.<br />Further optimizations were considered with caching, but ultimately skipped because of the sheer number of badge combinations.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/e38c48b5-4ccb-4e1f-8759-bf5f4efb4710.png" alt="" style="display:block;margin:0 auto" />

<h2>Usage</h2>
<p>The project is currently hosted <a href="https://badge.deathunter.com/">here</a>, and has an interactive live badge editor.<br />For API users, the endpoint for getting the list of technologies is <a href="https://badge.deathunter.com/techs">https://badge.deathunter.com/techs</a>,<br />and the list of badge backgrounds is: <a href="https://badge.deathunter.com/materials">https://badge.deathunter.com/materials</a></p>
<p>There are <strong>over 100</strong> different techs, each one being hand-drawn, including (but not limited to):<br />- Programming languages<br />- Frameworks<br />- Engines<br />- Tools/apps<br />- Browsers<br />- Operating Systems<br />- Website logos<br />- Company logos</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/af7ec165-3260-4686-b5b0-5c1c7f731c12.png" alt="" style="display:block;margin:0 auto" />]]></content:encoded></item><item><title><![CDATA[🚀 Base42 Recap 2025]]></title><description><![CDATA[Sorry for the late post — we travelled through the multiverse. Git conflicts were involved. Possibly on purpose.
There is a traditional marketing rule that says you should publish your yearly recap so]]></description><link>https://blog.42.mk/base42-recap-2025</link><guid isPermaLink="true">https://blog.42.mk/base42-recap-2025</guid><dc:creator><![CDATA[Pavlina Buchevska]]></dc:creator><pubDate>Tue, 03 Mar 2026 13:58:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/42e689a7-9092-47f3-ba75-b512459ddc2d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sorry for the late post — we travelled through the multiverse. Git conflicts were involved. Possibly on purpose.</p>
<p>There is a traditional marketing rule that says you should publish your yearly recap sometime in early January, ideally when everyone is still pretending they will stick to their New Year’s resolutions. As a hackerspace that lives in a garage and occasionally bends the laws of physics, we decided to ignore that rule completely. Instead, we are publishing our 2025 recap at the end of the Chinese New Year, as a small tribute to our lovely Asian community, Pagoda, which is an essential part of the Base42 story.</p>
<p>This year is the Year of the Fire Horse, a sign that appears only once every 60 years, last seen in 1966. The Fire Horse is associated with boldness, speed, high energy, and the potential for dramatic change. Honestly, that sounds suspiciously like our event calendar. Also, today is the Lantern Festival, which marks the official end of the New Year season before life returns to daily routines. The wish behind it remains simple and universal: peace and happiness for family and friends. Considering the state of the world, that wish feels more relevant than ever.</p>
<p>So this is officially our last chance for a recap. Let’s do it properly.</p>
<h2>🛠 About Base42</h2>
<p>Base42 is a basement hackerspace, born in a garage and named after The Hitchhiker's Guide to the Galaxy. As you may remember, 42 is the answer to life, the universe, and everything. Inspired by Douglas Adams, we decided that if 42 is the answer, then we might as well dedicate ourselves to building the question together. Our core principle is simple: together we are building the meaning, trying to create something good for society, for our friends, and for the open-source community.</p>
<p>The probability of success for a garage full of geeks attempting to influence the local tech culture was approximately 0.00000042 percent. The actual result was somehow, undeniably, yes.</p>
<p>The purpose of Base42 has never been just to host events. It is to create moments that make a difference, generate positive energy, and leave a lasting impact. It is a place with chill vibes and informal energy, the kind that reminds you why you fell in love with technology in the first place. It takes you back to the first time you built something from scratch, learned a new skill, or fixed a bug at 3 a.m. and felt proud to belong to the geeky world.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/32c935bf-8fea-46d6-bd37-6695404274a9.jpg" alt="" style="display:block;margin:0 auto" />

<h2>🎮 Global Game Jam</h2>
<p>We started 2025 the only logical way: with Global Game Jam organized by MGI, because many of us entered the world of technology through games in the first place. Before we were engineers, designers, cybersecurity experts, we were players trying to understand how things worked. Games were our first debugger and our first design document.</p>
<p>For 48 intense hours, around one hundred participants filled the garage with laptops, cables, caffeine, and dangerously ambitious ideas. Mechanics were invented at 2 a.m., redesigned at 4 a.m., and heroically patched at 7 a.m. It is the kind of event where you start by asking deep philosophical questions about gameplay, narrative, and whether 42 is truly the answer to life, the universe, and everything. By hour 36, however, the real question becomes whether the answer is actually a hot shower and six uninterrupted hours of sleep.</p>
<p>Somewhere between those two questions, magic happens. Strangers become teammates, ideas become prototypes, and exhaustion becomes a shared badge of honor. And by the end, when you stand there presenting something that did not exist 48 hours earlier, you remember exactly why you fell in love with building things in the first place.</p>
<p>The energy from that weekend could power a small country, or at least keep us motivated until the next registration opens.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/1b6d2b28-6e3f-4390-9434-bb9be091eb2b.jpg" alt="" style="display:block;margin:0 auto" />

<h2>🎨 UX/UI Conference</h2>
<p>Shortly after that, we helped organize the first major UX/UI conference together with the amazing crew from UXplore, led by a powerhouse team of brilliant ladies and supported by a few brave gentlemen who successfully survived rooms full of strong design opinions. With more than 250 participants, it marked a milestone for the design and tech communities in our country. Developers and designers shared one stage, and for a brief, beautiful moment, everyone agreed on spacing, typography, and user flows. We can confidently say the conference itself had excellent user experience, which might be the highest compliment a room full of designers can give.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/8bb32053-e63c-4d29-9967-5bab674daa3f.jpg" alt="" style="display:block;margin:0 auto" />

<h2>💔 A Difficult Spring</h2>
<p>As spring arrived, the tone of the year shifted. In March, tragedy struck Kochani, and the entire country was left in shock. Nearly 70 lives were lost, most of them young people, and for weeks it felt as if time itself had slowed down. In the face of tragedy, technology, events, and everyday discussions suddenly felt small in comparison to the weight of reality.</p>
<p>In May, together with the Macedonian .NET Community, we helped organize a humanitarian Global Azure Day in honor of the victims and in support of the affected families and survivors. Around 200 participants gathered not only to discuss cloud technologies and modern architectures, but also to donate and contribute in a meaningful way. It was powerful moment when the tech community came together not for networking or innovation, but for solidarity and compassion. It reminded us that behind every line of code, there are people — and that community matters most when it shows up for each other.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/4df7448d-4ea5-42ff-a7d4-4902a285982f.jpg" alt="" style="display:block;margin:0 auto" />

<h2>🤖 Meetups, AI &amp; Tech Communities</h2>
<p>Throughout the year, Base42 ran at full capacity with monthly meetups. PyData alone hosted nine gatherings, with artificial intelligence and large language models dominating the conversations. We discussed how AI is changing the way we build software, how to use it responsibly, and how to stay curious instead of scared. Some people were experimenting with fine-tuning models, others were integrating APIs into real products, and a few were just trying to explain to their parents that no, ChatGPT does not actually “know everything.”</p>
<p>And while AI sounded futuristic and complex, somehow the real drama still came from Python versions and broken environments. It turns out that even in the age of machine learning, the most powerful force in the universe might still be a properly configured virtual environment.</p>
<p>BeerJS, .NET meetups, cybersecurity sessions, AWS talks, Google Developers Group events, Angular Macedonia gatherings, ProdACT meetups, Microsoft Dynamics discussions, Advent of Code, and many more filled the calendar. Somewhere along the way, we stopped asking whether we should host another event and started asking how many chairs we needed this time.</p>
<p>Many weekends throughout the year, Base42 quietly transformed into a global Capture The Flag battleground. A dedicated group of cybersecurity enthusiasts gathered with one mission: compete at the highest level. Their focus and persistence paid off with impressive accomplishments and high scores on the global leaderboard. We are incredibly proud of this crew. They achieved impressive results and high rankings in global competitions, proving that a small garage can produce world-class talent.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/5a0ff51c-6115-40f5-9866-4b3adf8b66d9.jpg" alt="" style="display:block;margin:0 auto" />

<h2>💻 Doniraj Kompjuter &amp; Open Source</h2>
<p>One of our favorite communities, Doniraj Kompjuter, donated nearly 4,000 computers this year, helping bridge the digital gap and give devices a second life in the hands of students and families who truly need them. If you have an old laptop or computer collecting dust, bring it to Base42. We will make sure it finds a better purpose. On top of that, Doniraj Kompjuter donated an incredible server rack with 1TB of RAM to our space, which instantly upgraded our definition of “overkill” and “future-proof.”</p>
<p>With the new rack in place, we launched open-source projects into the wild, built a QR code generator, a competitive programming platform, a text editor, a new mobile application, and even worked on a drone project. Of course, there are also a few projects that are technically still not finished. They are not abandoned. They are simply in extended beta. Possibly waiting for the right alignment of planets. Or a free weekend.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/38181345-40a8-413d-989a-1833d7012222.webp" alt="" style="display:block;margin:0 auto" />

<h2>🎲 Gaming, Tabletop &amp; Community</h2>
<p>Somewhere between all of this, a new community rolled into Base42. Warden Gaming brought trading card games and board games into the garage with a simple but powerful mission: shuffling cards and making friends. What began as a few decks on a table became regular gatherings full of strategy, laughter, and very serious debates about rules that were absolutely interpreted correctly.</p>
<p>Every weekend, Warhammer armies are carefully assembled, painted with monk-like precision, and deployed across tabletops like miniature sci-fi operas. Strategies are calculated with the seriousness of production deployments, and dice rolls are treated like high-stakes system calls. Alongside the Warhammer battles, Zandana sessions brought even more tactical energy to the tables, while Dungeons &amp; Dragons campaigns opened portals to entirely different worlds. Character sheets were studied as intensely as documentation, and storytelling blended seamlessly with strategy.</p>
<p>It turns out that whether we are optimizing code, tuning AI models, or planning battlefield formations in a fantasy universe, geeks will optimize everything.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/f22b64f8-94b0-4699-ba58-8f08d937d801.jpg" alt="" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/732014eb-e9ed-4a5b-95a4-aea678f99ab7.jpg" alt="" style="display:block;margin:0 auto" />

<h2>🎤 What The Stack 2026</h2>
<p>Did we forget about the biggest event? No. We organized the biggest developer gathering of the year, an event that honestly deserves its own blog post. Together with DevEd, Angular Macedonia, and the entire IT community, we helped create a conference that brought together more than 800 people. There were four stages, 32 speakers, great coffee, beer (because engineers) music, and a game corner showcasing titles from Macedonian publishers. The afterparty escalated beautifully with rock and metal music by Why Not, and when speaker Domagoj unexpectedly grabbed a guitar and joined the stage, it became one of those legendary moments no schedule could have predicted. Students, senior programmers, engineers, and gamers all shared the same space, proving that the community is united by passion more than anything else.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/0581248c-3b04-48aa-b4d5-2c588203ab7a.jpg" alt="" style="display:block;margin:0 auto" />

<h2>🎮 Game Dev Rev &amp; Milestones</h2>
<p>Later in the year, we proudly supported Game Dev Rev, the first national game development conference organized with MGI and the local gaming community. The event brought together regional studios, indie developers, industry veterans, and representatives connected to the European gaming ecosystem. North Macedonia officially became part of The European Games Developer Federation. It is still slightly surreal that a small garage basement helped contribute to such a milestone. Apparently, even big federations sometimes start in small rooms.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/b4f3fcf4-a2dc-46bb-94f8-aba1517b3968.webp" alt="" style="display:block;margin:0 auto" />

<h2>🎨 Art, Podcasts &amp; Workshops</h2>
<p>Art found its permanent home in the garage as well. Three new graffiti pieces transformed the walls, thanks to Nikola. Pagoda organized four Gunpla workshops, which absolutely count as precision engineering. They organized a cyberpunk art exhibition and promoted Kalajdziev’s book. We welcomed two 3D art exhibitions and presentations by Zafir. Base42 became a place where soldering irons and spray cans coexisted peacefully.</p>
<p>Podcasts were recorded within our walls, including episodes by Debeli Gikovi, and we quietly started preparing our own geeky podcast. The studio is already painted, the microphones are set, and we are currently in the extremely complex process of choosing the name. As every developer knows, naming things is one of the hardest problems in computer science. Let’s just say, it is coming soon.</p>
<p>The year wrapped up with another game jam organized by Galactic Omnivore and MUGI, themed around white hats. During that same intense month, we organized an electronics workshop that sold out in a single day, with half of the participants being women, which made us especially proud.</p>
<p>We also hosted two cybersecurity workshops with Bozidar. The first sold out immediately, and the second lasted four full days with six trainers guiding participants deep into ethical hacking and digital defense. Neon lights. Hacking. Mind-blown.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/9012709e-5eb6-4463-9642-2ac474b36c05.jpg" alt="" style="display:block;margin:0 auto" />

<h2>🧠 Hackathons &amp; Ecosystem Support</h2>
<p>There were so many events happening that at times the calendar looked like a production server under heavy load. Among them, we hosted the Strip Trip Hackathon, where participants were pushed to think creatively under pressure and make things that were anything but ordinary. It was intense, chaotic in the best possible way, and full of ideas that refused to stay inside the box.</p>
<p>At the same time, we proudly supported our friends who organized the Ecommerce Conference and the AI Summit. Because for us, community is never competition. When one event grows, the entire ecosystem grows with it.</p>
<p>Some prototypes almost worked. Some projects definitely worked. A few experiments violated at least three known laws of physics. Thousands of humans and possibly a few aliens visited Base42. Thousands of coffees and beers were consumed. One arcade machine remains broken but emotionally supported. There was also at least one solution that mysteriously only worked on Fridays, and we decided not to question it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69750cce742fbb41c38fb161/c9f3dc11-05fb-4ff5-9768-fdbc4b0e6aba.jpg" alt="" style="display:block;margin:0 auto" />

<h2>🚀 Looking Ahead</h2>
<p>In 2026, we will continue our primary mission: figuring out the right questions. As Deep Thought once calculated, finding the ultimate answer takes time. Fortunately, we are patient, slightly chaotic, and well supplied with snacks.</p>
<p>We are already three months into 2026. We have organized new events: LAN party, Trivia Night, monthly meetups, chess tournament, internships for high school students who will end up teaching us…Many surprises are on the way. The universe is clearly not done with us yet.</p>
<p>Base42 remains a small garage with a jacuzzi and sauna unused for their original purpose, but it is also a living, breathing organism made of communities, ideas, experiments, friendships, and late-night discussions. It proves that you do not need a shiny building or a perfect marketing strategy to create impact. You need people, curiosity, courage, effort, and maybe a towel.</p>
<p>So thank you to every community that called Base42 home in 2025. Thank you to those who trusted us with your ideas, your events, and your time. Thank you to our friends who built conferences and invited us to support them. Thank you to everyone who shuffled a card, rolled a dice, wrote a line of code, painted a miniature, plugged in a server, recorded a podcast test episode, or stayed late just to help move chairs.</p>
<p>The universe may still be calculating the ultimate question, but one thing is clear: it is much more fun when we calculate it together.</p>
<p>And yes, the answer is still 42. ✨</p>
]]></content:encoded></item><item><title><![CDATA[Why I Made Jack (of All Trades)]]></title><description><![CDATA[ååWe all do it. Maybe you just started using curl instead of those GUI HTTP clients like Postman or Insomnia, and you had to copy the response body and paste it into a browser-based JSON formatter to make sense of it. Perhaps you wanted to inspect a ...]]></description><link>https://blog.42.mk/why-i-made-jack</link><guid isPermaLink="true">https://blog.42.mk/why-i-made-jack</guid><category><![CDATA[Linux]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Windows]]></category><category><![CDATA[cli]]></category><category><![CDATA[terminal]]></category><category><![CDATA[Kotlin]]></category><dc:creator><![CDATA[Gjorgji Dimeski]]></dc:creator><pubDate>Tue, 27 Jan 2026 14:20:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769428726022/2e0e0fd8-4658-4ca4-926e-51e57cab017b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ååWe all do it. Maybe you <em>just</em> started using curl instead of those GUI HTTP clients like Postman or Insomnia, and you had to copy the response body and paste it into a browser-based JSON formatter to make sense of it. Perhaps you wanted to inspect a JWT, so you go to jwt.io, and although the site says it doesn’t transmit tokens, pasting credentials into any webpage is a habit worth avoiding. Or you needed some IDs for those tests, so you googled “UUID generator“. Maybe you also wanted to generate a QR code - same story. Not to mention the ads you usually see on the websites.</p>
<p>Of course, there are various solutions for this - right in your terminal:</p>
<ul>
<li><p><code>jq</code> for JSON formatting and processing</p>
</li>
<li><p><code>qrencode</code> for generating QR codes</p>
</li>
<li><p><code>uuidgen</code>(Unix) and <code>New-Guid</code> (Windows) for all your UUID needs</p>
</li>
</ul>
<p>And you can always just google what you need and use some web app instead. I did that too, who am I to judge? But this is all too fractured and inconsistent.</p>
<p>I personally use some of these tools myself. And I’ve noticed a common trend among my colleagues: many default to web-based tools, even though terminal apps are often faster and easier once you get the hang of them. I sometimes recommend some specific CLI tool, but it rarely sticks. And with CLI tools there’s also the issue of compatibility across platforms, you can use one tool on your Linux system, but when you switch to Windows you need to suppress that muscle memory and write another command in PowerShell instead. It’s just too frustrating and fragmented.</p>
<p>Wouldn’t it just be easier to have a single tool, that you could install once on your system, would work across different platforms? No need to remember and install different tools, or search for alternatives for your Windows machine. Imagine it all centralized: <code>jack json</code>, <code>jack qr</code>, <code>jack uuid generate</code>, <code>jack jwt</code> - all just working, in a single package, across platforms.</p>
<p>That’s why I decided to make a sort of Swiss Army Knife tool for developers that’s portable and accessible. Initially, I actually named it factotum - a word for a general handyman. Then, my friend <a class="user-mention" href="https://hashnode.com/@ndinevski">Nikola Dinevski</a> came over and suggested <strong>jack</strong> - as in, jack of all trades. I thought that it was perfect! It’s exactly what it is, a multi-tool for all trades - but a master of none. What’s often omitted from that saying though, is that it’s oftentimes better than a master of one! Besides, jack is a pretty memorable name and easy to type in your terminal.</p>
<p>Jack removes the friction from remembering all those different tools, you just type in <code>jack</code> and find the tool you need, on all your systems! No need to install multiple tools, or lookup something online. It’s easy and intuitive to use, with no fancy and complicated options that you may find in more specific tools which, let’s be honest, you’ll only ever need a fraction of the time. It’s composable, simple, and helps with most of your day-to-day tools needs.</p>
<p>The intention is not to replace all the other tools I mentioned, of course they’ll have their use-cases. I don’t expect jack to have feature parity with dedicated tools. No, the aim is to have jack as the easy default tool you go to when you need something so you don’t have to juggle multiple tools.</p>
<p>Jack is made with Kotlin. <em>Why?</em> Because I want to be unique! No, but really - I wanted to try out the language for something else than backend services. It’s cross-platform, and is overall very nice to work with. Additionally, I had recently read about <a target="_blank" href="https://ajalt.github.io/clikt/">clikt</a>, a CLI library for Kotlin, and I wanted to take it for a spin.</p>
<p>I’d love for you to contribute! If you have a feature in mind you can always submit a PR or open an issue if you want me to put it on the to-do list. You can find the repository at <a target="_blank" href="https://github.com/dimeskigj/jack-cli">https://github.com/dimeskigj/jack-cli</a>.</p>
<p>Jack has a lot more features, and you can try them out for yourself!</p>
<p>On Linux/MacOS, you can install jack with:</p>
<pre><code class="lang-bash">curl -fsSL https://raw.githubusercontent.com/dimeskigj/jack-cli/main/scripts/install.sh | bash
</code></pre>
<p>If you’re on Windows, use PowerShell instead:</p>
<pre><code class="lang-powershell"><span class="hljs-built_in">iwr</span> https://raw.githubusercontent.com/dimeskigj/jack<span class="hljs-literal">-cli</span>/main/scripts/install.ps1 <span class="hljs-literal">-useb</span> | <span class="hljs-built_in">iex</span>
</code></pre>
<p>Let me know what you think! I would be glad if you find jack useful.</p>
]]></content:encoded></item><item><title><![CDATA[Announcing Base42 Advent of Code 2024]]></title><description><![CDATA[We’re excited to start the Base42 Advent of Code 2024 Competition, a unique event that combines programming challenges, friendly competition, and charitable giving. This year, Base42 is not only bringing you the thrill of solving coding puzzles but a...]]></description><link>https://blog.42.mk/announcing-base42-advent-of-code-2024</link><guid isPermaLink="true">https://blog.42.mk/announcing-base42-advent-of-code-2024</guid><category><![CDATA[Base42]]></category><category><![CDATA[42mk]]></category><category><![CDATA[AdventOfCode2024]]></category><category><![CDATA[AdventOfCode]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Competitive programming]]></category><category><![CDATA[Christmas]]></category><dc:creator><![CDATA[Ilija Boshkov]]></dc:creator><pubDate>Fri, 29 Nov 2024 14:32:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732887965286/1879f638-3cac-46f2-96bf-ade6a05c14d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We’re excited to start the <strong>Base42 Advent of Code 2024 Competition</strong>, a unique event that combines programming challenges, friendly competition, and charitable giving. This year, Base42 is not only bringing you the thrill of solving coding puzzles but also giving you a chance to make a real impact through donations to charitable causes.</p>
<p>Here’s everything you need to know about the competition, the rules, the prize pool, and how you can participate.</p>
<h2 id="heading-how-it-works">How It Works:</h2>
<ul>
<li><p><strong>Advent of Code</strong> is a yearly event that challenges coders to solve 25 puzzles over the course of December. Our competition is based around the official leaderboard for the event. Whether you’re a skilled programmer or just starting out, you’re invited to join us for a fun and impactful month of problem-solving.</p>
</li>
<li><p><strong>Leaderboard</strong>: Participants will be ranked on the official Advent of Code leaderboard based on their progress (number of stars earned) each day.</p>
</li>
<li><p><strong>Charity Focus</strong>: For each star you earn, <strong>Base42 will donate 5 MKD directly to a charity chosen by the first-place winner of the leaderboard.</strong></p>
</li>
<li><p><strong>Prize Pool</strong>: A portion of the prize pool will go to the top 3 participants based on their leaderboard performance, and another portion will be donated to charity.</p>
</li>
</ul>
<h2 id="heading-who-can-participate">Who Can Participate?</h2>
<ul>
<li><p><strong>Individual Participants</strong>:<br />  Anyone can join the competition and participate in the official Advent of Code leaderboard. Whether you're a beginner or an experienced developer, this event is open to everyone. You can compete for fun, or if you choose to pay the participation fee, you'll be eligible for the prize pool.</p>
</li>
<li><p><strong>Company Participation and Sponsorship</strong>:<br />  Companies are encouraged to get involved as sponsors and donors of the event! By sponsoring the competition, your company can contribute to the prize pool, which will be distributed among top participants and donated to charity.</p>
</li>
</ul>
<h2 id="heading-how-to-participate">How to participate</h2>
<ul>
<li><p>Join the <a target="_blank" href="https://discord.gg/424xxTZVYX">Discord server</a>, you can find the leaderboard join code in the #advent-of-code channel</p>
</li>
<li><p>Enter the leaderboard code you got from the Discord server into the <a target="_blank" href="https://adventofcode.com/2024/leaderboard/private">Advent of Code</a> website</p>
</li>
<li><p>Fill out the <a target="_blank" href="https://forms.gle/MNJoPoyj1fqKYVMdA">participation form</a></p>
</li>
<li><p>Pay the optional participation fee of 200 MKD by buying a participation ticket at <a target="_blank" href="https://drugastrana.mk/event/advent-of-code-42-2024/">this link</a>.</p>
</li>
<li><p>Code away for the next 25 days, and may the best coder win!</p>
</li>
</ul>
<h2 id="heading-prize-distribution">Prize Distribution:</h2>
<p>The total prize pool is divided as follows:</p>
<ul>
<li><p><strong>40% of the prize pool</strong> will be donated to charity, with the final recipient chosen by the first-place winner.</p>
</li>
<li><p><strong>60% of the prize pool</strong> will be split among the top 3 participants.</p>
<ul>
<li><p><strong>1st place</strong>: 50% of the prize pool</p>
</li>
<li><p><strong>2nd place</strong>: 30% of the prize pool</p>
</li>
<li><p><strong>3rd place</strong>: 20% of the prize pool<br />  In addition, for each star you earn during the Advent of Code, <strong>Base42 will donate 5 MKD directly to the charity of choice.</strong></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-participation-fee">Participation Fee:</h2>
<ul>
<li><p><strong>Optional Fee</strong>: There is a participation fee to enter the prize pool, which will go directly into the prize fund. <strong>The fee is 200 MKD</strong>. However, you can still join the leaderboard without paying the fee. If you choose not to pay, you'll be excluded from the prize pool but can still participate and compete for fun and donation.</p>
</li>
<li><p><strong>Deadline for entering the competition</strong>: December 15th.</p>
</li>
</ul>
<h2 id="heading-rules">Rules:</h2>
<ul>
<li><p><strong>Code of Ethics</strong>: While AI tools are prohibited in the competition, we recognize that it’s not always possible to prevent the use of such tools. We trust participants to follow the honor system and compete fairly. Please keep the spirit of the event in mind and avoid using AI to solve the puzzles. Let’s keep it a challenge of skill and creativity!</p>
</li>
<li><p><strong>Joining the Competition</strong>: To enter, join the <a target="_blank" href="https://discord.gg/424xxTZVYX">Base42 Discord server</a>, sign up, and join the official Advent of Code leaderboard.</p>
</li>
<li><p><strong>Sponsorship</strong>: Companies are invited to sponsor the event and contribute to the prize pool. Sponsors can contribute any amount, and their donation will be added to the prize pool, following the same distribution rules as above.</p>
</li>
<li><p><strong>Charity Selection</strong>: The first-place winner will have the privilege of selecting the charity that will receive the 40% donation from the prize pool as well as the donation of the contributions from stars.</p>
</li>
<li><p><strong>Fair Play</strong>: Participants are expected to compete with integrity and in accordance with the rules of Advent of Code. Any attempt to exploit loopholes or cheat will result in disqualification.</p>
</li>
<li><p><strong>Online</strong>: The competition will mostly be online as it's very long running, but you're welcome and encouraged to group up and come by in Base42 IRL and solve challenges together.</p>
</li>
</ul>
<h2 id="heading-get-involved">Get Involved:</h2>
<ul>
<li><p><strong>Compete</strong>:<br />  Solve the daily puzzles, earn stars, and climb the leaderboard. Every star counts towards increasing our charity donation!</p>
</li>
<li><p><strong>Support a Cause</strong>:<br />  Help us make a real difference in the world by contributing through your coding skills. Every time you solve a puzzle, you’re helping to fund a charity chosen on the final day.</p>
</li>
<li><p><strong>Sponsor the Event</strong>:<br />  If you're part of a company that wants to get involved, consider sponsoring the competition! For more info, contact us at <a target="_blank" href="mailto:base42mk@gmail.com">base42mk@gmail.com</a>.</p>
</li>
</ul>
<h2 id="heading-why-participate">Why Participate?</h2>
<ul>
<li><p><strong>For the Thrill of Coding</strong>:<br />  Challenge yourself with daily puzzles and see how you stack up against other talented developers.</p>
</li>
<li><p><strong>For a Good Cause</strong>:<br />  Not only will you be competing for great prizes, but you'll also be contributing to a worthy charity chosen by the first-place winner.</p>
</li>
<li><p><strong>For Being Awesome</strong>:<br />  Do you even need a reason to flex your 1337 coding skillz?</p>
</li>
</ul>
<p>We can’t wait to see who takes the top spot this year and to see how much we can raise for charity!<br />Let’s make this the best Advent of Code yet.<br />Good luck and happy hacking!</p>
<h2 id="heading-questions">Questions?</h2>
<p>If you have any questions about the competition or need more information on how to join, feel free to reach out to us on the Discord server, social media, or via email at <a target="_blank" href="mailto:base42mk@gmail.com">base42mk@gmail.com</a>.<br />Let’s code for a cause!</p>
]]></content:encoded></item></channel></rss>