<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Cedd Burge - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Cedd Burge - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:23:56 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/ceddlyburge/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Optimize a Graphical React Codebase — Optimize d3-zoom and dnd-kit Code ]]>
                </title>
                <description>
                    <![CDATA[ Miro and Figma are online collaborative canvas tools that became very popular during the pandemic. Instead of using sticky notes on a physical wall, you can add a virtual post—and an array of other things—to a virtual canvas. This lets teams collabor... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-optimize-a-graphical-react-codebase/</link>
                <guid isPermaLink="false">68f0e6e94d664bb368c83f39</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Thu, 16 Oct 2025 12:36:57 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760617553059/99fd830f-39a8-4067-9727-e9b35850168d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Miro and Figma are online collaborative canvas tools that became very popular during the pandemic. Instead of using sticky notes on a physical wall, you can add a virtual post—and an array of other things—to a virtual canvas. This lets teams collaborate virtually in ways that feel familiar from the physical world.</p>
<p>I previously wrote an article showing <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-a-figma-miro-style-canvas-with-react-and-typescript/">how to create a Figma/Miro Clone in React and TypeScript</a>. The <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3">code in the article</a> was designed to be as easy to understand, and in this article, we’re going to optimize it. The code used <a target="_blank" href="https://dndkit.com/">DndKit</a> for dragging and dropping, and <a target="_blank" href="https://github.com/d3/d3-zoom">D3 Zoom</a> for panning and zooming. There were four components (<code>App</code>, <code>Canvas</code>, <code>Draggable</code> and <code>Addable</code>), and about 250 lines of code. You do not need to read the original article to understand this one.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759863968693/38c8784c-47c8-46e0-9f06-0567c0ebf668.gif" alt="38c8784c-47c8-46e0-9f06-0567c0ebf668" class="image--center mx-auto" width="1026" height="730" loading="lazy"></p>
<p>Standard optimizations such as <code>useCallback</code>, <code>memo</code>, and similar made it about twice as fast when dragging, but made no difference for panning and zooming. More creative/intensive optimizations made it about ten times as fast in most cases.</p>
<p>You can see the optimized code on <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised">GitHub</a> and there is a <a target="_blank" href="https://ceddlyburge.github.io/react-figma-miro-canvas-optimised/">live demo on GitHub pages</a> to test out the speed with 100,000 cards.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-measure-performance-in-react-apps">How to Measure Performance in React Apps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-investigate-the-performance">How to Investigate the performance</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-panning-and-zooming-the-canvas">How to Optimize Panning and Zooming the Canvas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-dragging-cards-around-the-canvas">How to Optimize Dragging Cards Around the Canvas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-results">Results</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-how-to-measure-performance-in-react-apps">How to Measure Performance in React Apps</h2>
<p>There are three common ways to measure performance in React Apps</p>
<ul>
<li><p><a target="_blank" href="https://chromewebstore.google.com/detail/react-developer-tools">React Dev Tools profiler</a></p>
</li>
<li><p><a target="_blank" href="https://developer.chrome.com/docs/devtools/performance/overview">Chrome Dev Tools profiler</a>, especially using <a target="_blank" href="https://www.debugbear.com/blog/favourite-devtools-features-in-2025#add-custom-tracks-to-performance-recordings">custom tracks</a></p>
</li>
<li><p><a target="_blank" href="https://react.dev/reference/react/Profiler">Profiler component</a></p>
</li>
</ul>
<p>These tools are all great, but none of them are quite the right fit in this case. In most codebases, the time spent executing JavaScript code (both our code and that of the React framework) is the primary issue. However, after all your code has run and React has updated the Dom, the browser still has a lot of work to do:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760025146277/7ae9ef2a-e248-491b-a07d-c674694d3fa9.png" alt="7ae9ef2a-e248-491b-a07d-c674694d3fa9" class="image--center mx-auto" width="898" height="219" loading="lazy"></p>
<p>In this case, this browser layout and rendering time was significant, and is not accounted for by the React profiling.</p>
<p>You can use custom tracks in the Chrome dev tools profiler, but it is very cumbersome to use.</p>
<p>For us, the JavaScript <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance">performance API</a> is the best option, which gives results that are closer to those experienced by the user, and is relatively easy to use.</p>
<p>First, we make a call to <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark"><code>performance.mark</code></a> in the event handler that starts the action, with a string to describe the time point. For example, when starting a zoom or pan operation:</p>
<pre><code class="lang-javascript">zoomBehavior.on(<span class="hljs-string">"start"</span>, <span class="hljs-function">() =&gt;</span> {
    performance.mark(<span class="hljs-string">'zoomingOrPanningStart'</span>);
}
</code></pre>
<p>Then, in a <code>useEffect</code> hook, we call <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark"><code>performance.mark</code></a> again, and call <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure"><code>performance.measure</code></a> to calculate the time between the two points:</p>
<pre><code class="lang-javascript">useEffect(<span class="hljs-function">() =&gt;</span> {
    performance.mark(<span class="hljs-string">'zoomingOrPanningEnd'</span>);
    performance.measure(<span class="hljs-string">'zoomingOrPanning'</span>, <span class="hljs-string">'zoomingOrPanningStart'</span>, <span class="hljs-string">'zoomingOrPanningEnd'</span>);
});
</code></pre>
<p>The <a target="_blank" href="https://react.dev/reference/react/useEffect">React docs</a> states that <code>useEffect</code> usually fires after the browser has painted the updated screen, which is what we want.</p>
<p>This isn't perfect, and will vary depending on the machine specifications, and what else the machine is doing at the time, but it was good enough to verify which optimizations worked best. It is possible to go further if you need to. For example, using <a target="_blank" href="https://filiphric.com/testing-frontend-performance-with-cypress">Cypress to automate and profile scenarios</a>, potentially running many times to get a good mean, or using <a target="_blank" href="https://www.browserstack.com/guide/performance-testing-with-cypress">Browserstack to test on a variety of devices</a>.</p>
<h2 id="heading-how-to-investigate-the-performance">How to Investigate the Performance</h2>
<p>Most of the investigation involved using the <a target="_blank" href="https://chromewebstore.google.com/detail/react-developer-tools">React Dev Tools profiler</a> to record profiles of user interactions.</p>
<p>The performance data shows how many commits there were in the profile, and how long each one took, which is a great way to see if there are too many commits.</p>
<p>Each commit displays a flame chart showing which components rendered and why they re-rendered. This makes it much easier to find ways to avoid the re-rendering, and to check that memoization strategies are working as expected. This does have some caveats though. It often says <a target="_blank" href="https://github.com/facebook/react/issues/19732">'The parent component rendered'</a>, which is misleading default text for when it doesn’t understand what happened (and is often due to a change in a parent context). It also says things like 'hook 9 changed', which makes it time consuming to work out exactly which hook changed.</p>
<p>The flame chart also shows how long each component took to render. This helps target problem components that we need to focus on.</p>
<h2 id="heading-how-to-optimize-panning-and-zooming-the-canvas">How to Optimize Panning and Zooming the Canvas</h2>
<p>The original <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3/blob/main/src/Canvas.tsx">Canvas</a> element used the CSS transform <code>translate3d(x, y, k)</code> to pan and zoom the canvas. This works, but it doesn't scale child elements, so when the zoom changes, all the cards on the canvas have to be re-rendered with a new CSS transform for the new zoom level (<a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3/blob/97935f5b03ecff2f0732f6138e173a0c5e71a1ed/src/Draggable.tsx#L31"><code>scale(${canvasTransform.k})</code></a>).</p>
<pre><code class="lang-javascript"> &lt;div
    ...
    className=<span class="hljs-string">"canvas"</span>
    style={{
        <span class="hljs-attr">transform</span>: <span class="hljs-string">`translate3d(<span class="hljs-subst">${transform.x}</span>px, <span class="hljs-subst">${transform.y}</span>px, <span class="hljs-subst">${transform.k}</span>px)`</span>,
        ...
    }}&gt;
    ...
&lt;/div&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>
    <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">...</span>
        <span class="hljs-attr">transform:</span> `<span class="hljs-attr">scale</span>(${<span class="hljs-attr">canvasTransform.k</span>})`,
    }}&gt;</span>
    ...
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>I changed this to use <code>translateX(x) translateY(y) scale(k)</code>, which has the same effect, but does scale child elements. This way, when the zoom changes, none of the cards will be re-rendered (the <code>style</code> of the <code>card</code> component no longer uses the <code>canvasTransform.k</code>).</p>
<pre><code class="lang-javascript"> &lt;div
    ...
    className=<span class="hljs-string">"canvas"</span>
    style={{
        <span class="hljs-attr">transform</span>: <span class="hljs-string">`translateX(<span class="hljs-subst">${transform.x}</span>px) translateY(<span class="hljs-subst">${transform.y}</span>px) scale(<span class="hljs-subst">${transform.k}</span>)`</span>,
        ...
    }}&gt;
    ...
&lt;/div&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>
    <span class="hljs-attr">...</span>
&lt;/<span class="hljs-attr">div</span>&gt;</span></span>
</code></pre>
<p>The <code>Canvas</code> still needed to re-render whenever the pan or zoom changed, and it is possible to prevent this with <code>useRef</code>, and updating the CSS transform with direct JavaScript Dom manipulation in the <a target="_blank" href="https://d3js.org/d3-zoom">d3-zoom</a> event handler. This doesn’t make a significant improvement to the performance though, and is a definite hack, so the trade off is not worthwhile.</p>
<p>Both zooming and panning get a bit slower when the canvas is zoomed very far out and there are (a lot) more cards visible on the screen, just due to the browser having to render them all. It's still workable at 100,000 cards though. There are things you can do about this. An easy option is limiting the maximum zoom extent. This is a functional change, so potentially something that doesn’t meet requirements, but it is easy to do in d3-zoom using <a target="_blank" href="https://d3js.org/d3-zoom#zoom_scaleExtent"><code>scaleExtent</code></a>:</p>
<pre><code class="lang-javascript">zoom&lt;HTMLDivElement&gt;().scaleExtent([<span class="hljs-number">0.1</span>, <span class="hljs-number">100</span>])
</code></pre>
<p>Another option is to create a bitmap for very low zoom levels and render that as a single element. This may be difficult, but it means that there will be no change to the functionality.</p>
<h2 id="heading-how-to-optimize-dragging-cards-around-the-canvas">How to Optimize Dragging Cards Around the Canvas</h2>
<h3 id="heading-starting-a-drag"><strong>Starting a drag</strong></h3>
<p>The <code>useDraggable</code> hook from <code>DndContext</code> causes some re-renders when starting a drag operation.</p>
<p>It is possible to improve this by changing the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component to just have this hook (and the things that use it) and having a <code>DraggableInner</code> component for everything else (inside a <code>memo</code>). This works well for reducing the re-renders, in that the <code>DraggableInner</code> almost never get re-rendered, and improves the speed of starting a drag operation. However, it was still fairly slow, and the time was all under the <code>DndContext</code>.</p>
<p>A better option is to create a new <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> component, that looks exactly like the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component, but does not hook up with <code>DndContext</code>. These cards are shown on the Canvas, and have an <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/56d0c8256350ef3a0d8f50cc442305bd6c9d03c1/src/NonDraggable.tsx#L10"><code>onMouseEnter</code></a> event, to swap in the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component for the active card, so that dragging continued to work.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> onMouseEnter = useCallback(<span class="hljs-function">() =&gt;</span> {
    setHoverCard(card);
}, []);
</code></pre>
<p>This works well, and significantly improves the speed when starting a drag operation, but it was still quite slow with large numbers of cards. Nearly nothing was getting re-rendered, but there is still a time cost to when using <code>memo</code>, as it needs to check whether components have changed.</p>
<p>To fix this, we create an <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/AllCards.tsx"><code>AllCards</code></a> component, that contains all the cards on the canvas as <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> components. Because it always renders all the cards, it nearly never needs to be re-rendered, and it is used with <code>memo</code>. So instead of each individual card using a <code>memo</code> (with the associated time cost), there is now just one component using a <code>memo</code>. To make it so that the dragging still works, the active <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component is rendered on top, obscuring the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> component beneath it. There is also a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Cover.tsx"><code>Cover</code></a> component beneath that, so that when the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component is dragged away, the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> component underneath remains hidden.</p>
<p>Original code, where each card is a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component:</p>
<pre><code class="lang-javascript">&lt;DndContext ...&gt;
    {cards.map(<span class="hljs-function">(<span class="hljs-params">card</span>) =&gt;</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Draggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{card}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{card.id}</span> <span class="hljs-attr">canvasTransform</span>=<span class="hljs-string">{transform}</span> /&gt;</span></span>
    ))}
&lt;/DndContext&gt;
</code></pre>
<p>Optimized code, where the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/AllCards.tsx"><code>AllCards</code></a> component renders all the cards as <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> components, and then a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Cover.tsx"><code>Cover</code></a> and a <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component for the active card.</p>
<pre><code class="lang-javascript">&lt;AllCards cards={cards} setHoverCard={setHoverCard} /&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">DndContext</span> <span class="hljs-attr">...</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Cover</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{hoverCard}</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Draggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{hoverCard}</span> <span class="hljs-attr">canvasTransform</span>=<span class="hljs-string">{transform}</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">DndContext</span>&gt;</span></span>
</code></pre>
<p>This works very well. With a low number of cards, the speed is about the same, but with a high numbers of cards, it’s about twenty times faster.</p>
<p>There is now a new potential performance issue with the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/56d0c8256350ef3a0d8f50cc442305bd6c9d03c1/src/NonDraggable.tsx#L10"><code>onMouseEnter</code></a> event that swaps in the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/Draggable.tsx"><code>Draggable</code></a> component for the active card, but this just adds two components to the Dom, and is very quick even with large numbers of cards.</p>
<h3 id="heading-finishing-a-drag"><strong>Finishing a drag</strong></h3>
<p>Finishing a drag operation is hard to optimize, as the position of a card changes, and that does need to re-render, which means that the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/AllCards.tsx"><code>AllCards</code></a> component has to re-render as well.</p>
<p>You can see original code below. Even when using <code>memo</code> with the Draggable component, the end drag operation still takes 2500ms with 100,000 cards, mostly due to the complexity of the <code>Draggable</code> component and its integration with DndKit.</p>
<pre><code class="lang-javascript">&lt;DndContext ...&gt;
    {cards.map(<span class="hljs-function">(<span class="hljs-params">card</span>) =&gt;</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Draggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{card}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{card.id}</span> <span class="hljs-attr">canvasTransform</span>=<span class="hljs-string">{transform}</span> /&gt;</span></span>
    ))}
&lt;/DndContext&gt;
</code></pre>
<p>However, we now use the <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-optimised/blob/main/src/NonDraggable.tsx"><code>NonDraggable</code></a> components, which all <code>memo</code> successfully, and only the dragged card is re-rendered. There is still a time cost using the <code>memo</code>, and this is the slowest part of the solution, but it leads to an increase in speed to 500ms with 100,000 cards.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> NonDraggable = memo(...)

<span class="hljs-keyword">const</span> AllCards = memo(<span class="hljs-function">(<span class="hljs-params">cards, setHoverCard</span>) =&gt;</span> {
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
        {cards.map((card) =&gt; {
            <span class="hljs-tag">&lt;<span class="hljs-name">NonDraggable</span> <span class="hljs-attr">card</span>=<span class="hljs-string">{card}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{card.id}</span> <span class="hljs-attr">setHoverCard</span>=<span class="hljs-string">{setHoverCard}</span> /&gt;</span>);
        })}
    <span class="hljs-tag">&lt;/&gt;</span></span>;
});
</code></pre>
<h2 id="heading-results"><strong>Results</strong></h2>
<p>The base unoptimized version started to get slow between 1000 and 5000 cards. Standard optimizations improved this to around 10,000 cards, and the more optimization took it to about 100,000 cards. The trade off is that the code becomes significantly more complicated, which makes it harder to understand and modify, especially for people new to the codebase.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td></td><td><strong>Pan (ms)</strong></td><td><strong>Zoom (ms)</strong></td><td><strong>Start drag (ms)</strong></td><td><strong>End drag (ms)</strong></td><td><strong>Card hover (ms)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>1000 cards</td><td>Base</td><td>3</td><td>4</td><td>200</td><td>50</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>2</td><td>3</td><td>200</td><td>30</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>10</td><td>10</td><td>7</td><td>15</td><td>2</td></tr>
<tr>
<td>5000 cards</td><td>Base</td><td>20</td><td>150</td><td>450</td><td>200</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>20</td><td>150</td><td>200</td><td>80</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>10</td><td>10</td><td>25</td><td>40</td><td>2</td></tr>
<tr>
<td>10,000 cards</td><td>Base</td><td>50</td><td>300</td><td>900</td><td>400</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>50</td><td>300</td><td>400</td><td>180</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>25</td><td>25</td><td>50</td><td>50</td><td>2</td></tr>
<tr>
<td>50,000 cards</td><td>Base</td><td>1000</td><td>1500</td><td>4000</td><td>1800</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>1000</td><td>1500</td><td>1900</td><td>900</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>150</td><td>150</td><td>150</td><td>250</td><td>5</td></tr>
<tr>
<td>100,000 cards</td><td>Base</td><td>-</td><td>-</td><td>-</td><td>-</td><td>-</td></tr>
<tr>
<td></td><td>Basic optimization</td><td>3000</td><td>4500</td><td>5000</td><td>2500</td><td>-</td></tr>
<tr>
<td></td><td>Intensive optimization</td><td>150</td><td>250</td><td>300</td><td>500</td><td>15</td></tr>
</tbody>
</table>
</div><h2 id="heading-summary">Summary</h2>
<p>It is unusual to display 100,000 or more items on screen in a standard React App, but in a highly graphical codebase, it becomes much more likely.</p>
<p>With these numbers, the browser rendering engine is likely to take a significant amount of time, so it is best to use the performance API to measure performance, instead of the usual React tools.</p>
<p>Standard React optimization strategies do work and improve the situation, but there is a need to go further, by finding ways to avoid renders, and even to avoid too many <code>memo</code> comparisons.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Figma / Miro Style Canvas with React and TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ Miro and Figma are online collaborative canvas type tools that became very popular during the pandemic. Instead of sticking post it notes on a physical wall, you can now add virtual post its (and a dizzying array of other things) to a virtual canvas.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-figma-miro-style-canvas-with-react-and-typescript/</link>
                <guid isPermaLink="false">66bb925d867a396452a80288</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Sat, 16 Mar 2024 12:59:43 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727437264136/a99a5516-2b24-40b2-a22f-973b28043f07.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Miro and Figma are online collaborative canvas type tools that became very popular during the pandemic.</p>
<p>Instead of sticking post it notes on a physical wall, you can now add virtual post its (and a dizzying array of other things) to a virtual canvas. This lets teams collaborate virtually in ways that feel familiar from the physical world.</p>
<p>Figma and Miro are large, mature products, and they bypass HTML and CSS entirely. They use technologies like WebAssembly, WebGL, C++ and similar, due to their extremely demanding performance requirements.</p>
<p>But we can create similar virtual canvas type features, without the complexity, using React, TypeScript, and a couple of packages. We are going to support one type of 'card', that will just contain simple text, to keep this guide concise, but it is easy to extend the solution to support more elaborate use cases.</p>
<p>The functionality we are going to implement is:</p>
<ul>
<li><p>Dragging cards around the canvas</p>
</li>
<li><p>Adding new cards to the canvas</p>
</li>
<li><p>Panning and Zooming the canvas</p>
</li>
</ul>
<p>This article is a step by step guide describing how to create these features, and there is companion code on GitHub with live demos.</p>
<p>Our scratch-built solution won't be as fast as Figma or Miro, but if you're needs are simpler, it'll probably be enough.</p>
<h2 id="heading-project-overview">Project Overview</h2>
<p>We'll use <a target="_blank" href="https://dndkit.com/">DndKit</a> for dragging and dropping and <a target="_blank" href="https://github.com/d3/d3-zoom">D3 Zoom</a> for panning and zooming. I found both of these tools to be a pleasure to work with.</p>
<p>The <a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3">code is on GitHub</a>, and there is a <a target="_blank" href="https://ceddlyburge.github.io/react-figma-miro-canvas-part-3/">live demo</a> so you can try it out.</p>
<p>There are only 4 components (<code>App</code>, <code>Canvas</code>, <code>Draggable</code> and <code>Addable</code>), and only about 250 lines of code.</p>
<p>This guide is aimed at intermediate level React / TypeScript developers. It will be much easier if you already understand <a target="_blank" href="https://react.dev/learn/your-first-component#defining-a-component">components</a>, <a target="_blank" href="https://react.dev/blog/2023/03/16/introducing-react-dev#going-all-in-on-modern-react-with-hooks">hooks</a>, <a target="_blank" href="https://react.dev/learn/thinking-in-react">how to think in React</a>, <a target="_blank" href="https://react.dev/reference/react/useState">state</a>, <a target="_blank" href="https://react.dev/reference/react/useRef">mutable refs</a> and things like that.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/part-3.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Screen grab showing final canvas solution</em></p>
<p>Alright, now to see how you can actually build this, let's dive in.</p>
<h2 id="heading-step-1-how-to-drag-the-cards-around-the-canvas"><strong>Step 1 – How to Drag the Cards Around the Canvas</strong></h2>
<p>To get started, we'll use <a target="_blank" href="https://dndkit.com/">DndKit</a> to drag cards around a canvas. We'll install the tools we need to build the project, create a simple <code>Card</code> type, and create simple <code>App</code>, <code>Canvas</code> and <code>Draggable</code> components.</p>
<p>The <code>App</code> component stores the <code>card</code> state and renders the <code>Canvas</code>.</p>
<p>The <code>Canvas</code> component integrates with DndKit, updates the <code>card</code> state and renders the <code>cards</code> as <code>Draggable</code> components.</p>
<p>The <code>Draggable</code> component integrates with DndKit and uses CSS styling to position itself correctly on the canvas.</p>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-1">Step 1 Code on GitHub.</a></p>
<p>Here's a screen grab of what we'll be building in this part, and there is also a <a target="_blank" href="https://ceddlyburge.github.io/react-figma-miro-canvas-part-1/">live demo</a> you can try out for yourself:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/part-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Screen grab showing part 1 of the canvas solution</em></p>
<h3 id="heading-project-setup">Project Setup</h3>
<p>We create a new project with Vite and install DndKit using the following commands:</p>
<pre><code class="lang-bash">npm create vite@latest figma-miro-canvas -- --template react-ts
npm install
npm install @dnd-kit/core
</code></pre>
<h3 id="heading-apptsx"><strong>App.tsx</strong></h3>
<p>The <code>App</code> component stores the <code>card</code> state and renders the <code>Canvas</code>.</p>
<p>We add a <code>Card</code> type, in this demo cards will simply contain some text. <code>UniqueIdentifier</code> is from DndKit, and looks scary, but it's just a <code>String | number</code>. <code>Coordinates</code> also comes from DndKit and contains the x and y position.</p>
<pre><code class="lang-tsx">export type Card = {  
  id: UniqueIdentifier;
  coordinates: Coordinates;
  text: string;
};
</code></pre>
<p>We then need to create some cards, and pass them to the canvas.</p>
<pre><code class="lang-tsx">export const App = () =&gt; {
  const [cards, setCards] = useState&lt;Card[]&gt;([
    { id: "Hello", coordinates: { x: 0, y: 0 }, text: "Hello" },
  ]);

  return (&lt;Canvas cards={cards} /&gt;);
}
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-1/blob/main/src/App.tsx">App.tsx on GitHub</a></p>
<h3 id="heading-canvastsx"><strong>Canvas.tsx</strong></h3>
<p>The <code>Canvas</code> component integrates with DndKit, updates the <code>card</code> state and renders the <code>cards</code> as <code>Draggable</code> components.</p>
<p>It takes <code>cards</code> and <code>setCards</code> as props, as the state is stored higher up the tree in <code>App</code>. This isn't strictly necessary right now, but is useful in later steps.</p>
<pre><code class="lang-tsx">type Props = {
  cards: Card[];
  setCards: (cards: Card[]) =&gt; void;
}
</code></pre>
<p>We add a function to call <code>setCards</code> with an update after a drag operation has completed. This simply adds the drag distance / delta to the x and y values for the card that was being dragged.</p>
<p>The <code>DragEndEvent</code> comes from DndKit, and includes the <code>active</code> item being dragged, so we can use that to work out which <code>card</code> to update.</p>
<pre><code class="lang-tsx">const updateDraggedCardPosition = ({ delta, active }: DragEndEvent) =&gt; {
  if (!delta.x &amp;&amp; !delta.y) return;

  setCards(
    cards.map((card) =&gt; {
      if (card.id === active.id) {
        return {
          ...card,
          coordinates: {
            x: card.coordinates.x + delta.x,
            y: card.coordinates.y + delta.y,
          },
        };
      }
      return card;
    })
  );
};
</code></pre>
<p>We add a <code>div</code> to represent the canvas, a <code>DndContext</code> (from DndKit), and render a <code>Draggable</code> for each card. We hook up our new function with the <code>onDragEnd</code> event from <code>DndContext</code> so that the <code>card</code> state is updated after a successful drag operation.</p>
<pre><code class="lang-tsx">&lt;div className="canvas"&gt;
  &lt;DndContext onDragEnd={updateDraggedCardPosition}&gt;
    {cards.map((card) =&gt; (
      &lt;Draggable card={card} key={card.id} /&gt;
    ))}
  &lt;/DndContext&gt;
&lt;/div&gt;
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-1/blob/main/src/Canvas.tsx">Canvas.tsx on GitHub</a></p>
<h3 id="heading-draggabletsx"><strong>Draggable.tsx</strong></h3>
<p>The <code>Draggable</code> component integrates with DndKit and uses CSS styling (<code>position</code>, <code>top</code> and <code>left</code>) to position itself correctly on the canvas.</p>
<p><code>useDraggable</code> comes from DndKit, and we blindly pass the <code>attributes</code>, <code>listeners</code>, and <code>setNodeRef</code> that it returns to our <code>div</code>, which allows it to respond to <code>onClick</code> events and things like that.</p>
<p>We also use the <code>transform</code> from DndKit to apply CSS in order to render the card at a modified position when a drag is in progress. Slightly confusingly, the CSS property name is also called <code>transform</code>.</p>
<pre><code class="lang-tsx">export const Draggable = ({ card }: { card: Card }) =&gt; {
  // hook up to DndKit
  const { attributes, listeners, setNodeRef, transform } = useDraggable({
    id: card.id,
  });

  return (
    &lt;div
      className="card"
      style={{
        // position card on canvas
        position: "absolute",
        top: `${card.coordinates.y}px`,
        left: `${card.coordinates.x}px`,
        // temporary change to this position when dragging
        ...(transform
          ? {
              transform: `translate3d(${transform.x}px, ${transform.y}px, 0px)`,
            }
          : {}),
      }}
      ref={setNodeRef}
      {...listeners}
      {...attributes}
    &gt;
      {card.text}
    &lt;/div&gt;
  );
};
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-1/blob/main/src/Draggable.tsx">Draggable.tsx on GitHub</a></p>
<h3 id="heading-appcss"><strong>App.css</strong></h3>
<p>Finally we can add some CSS styling to make everything look vaguely acceptable. This isn't strictly necessary, but it does look a lot better, even with my limited design skills.</p>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-1/blob/main/src/App.css">App.css on GitHub</a></p>
<h2 id="heading-step-2-how-to-add-new-cards-to-the-canvas"><strong>Step 2 – How to Add New Cards to the Canvas</strong></h2>
<p>In this step, we'll create a new <code>Addable</code> component, to represent cards that are not currently on the canvas, but can be dragged on to it.</p>
<p>We will update <code>App</code> to add a "tray" <code>div</code> to contain these new <code>Addable</code> cards. We will also add <em>another</em> DndContext (this new <code>DndContext</code> will handle the drag drop from the tray to the canvas, and the existing <code>DndContext</code> in <code>Canvas</code> handles dragging of cards around the canvas), and hook up to its events. This will let us update the state when the <code>Addable</code> cards are drag / dropped on to the canvas.</p>
<p>We will update <code>Canvas</code> to make it a DndKit drop target, so that the <code>Addable</code> cards can be dropped on to it.</p>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-2">Step 2 code on GitHub</a></p>
<p>Here's the functionality we'll add in this section, and there is also a <a target="_blank" href="https://ceddlyburge.github.io/react-figma-miro-canvas-part-1/">live demo</a> you can try out for yourself:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/part-2.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-apptsx-1"><strong>App.tsx</strong></h3>
<p>The <code>App</code> component now needs a "tray" <code>div</code> to contain <code>Addable</code> cards.</p>
<p>It also needs <em>another</em> DndContext (this new <code>DndContext</code> will handle the drag drop from the tray to the canvas, and the existing <code>DndContext</code> in <code>Canvas</code> handles dragging of cards around the canvas). It needs to hook up to its events so that we can update the state when the <code>Addable</code> cards are drag / dropped on to the canvas.</p>
<p>We add a list of cards to appear on the tray:</p>
<pre><code class="lang-tsx">const trayCards = [
  // the coordinates aren't used for the tray cards, we could create a new type without them
  { id: "World", coordinates: { x: 0, y: 0 }, text: "World" },
];
</code></pre>
<p>We add a function to work out the position on the canvas at the end of a drag drop operation. This has to know the initial position of the card, the drag distance / delta, and the top left position of the canvas. These details are all provided by the DndKit <code>DragEndEvent</code>.</p>
<pre><code class="lang-tsx">const calculateCanvasPosition = (
  initialRect: ClientRect,
  over: Over,
  delta: Translate
): Coordinates =&gt; ({
  x: initialRect.left + delta.x - (over?.rect?.left ?? 0),
  y: initialRect.top + delta.y - (over?.rect?.top ?? 0),
});
</code></pre>
<p>We add state to store the tray card being dragged, along with a function to update the <code>cards</code> state after a drag / drop from the tray. The <code>DragEndEvent</code> comes from DndKit, and includes the <code>active</code> item being dragged, so we can use that to create a new <code>card</code> and add it to the <code>cards</code> array. We also make some checks to make sure that the "canvas" is the drop target, and that we have all the data we need.</p>
<pre><code class="lang-tsx">const [draggedTrayCardId, setDraggedTrayCardId] = useState&lt;UniqueIdentifier | null&gt;(null);

const addDraggedTrayCardToCanvas = ({ over, active, delta }: DragEndEvent) =&gt; {
  setDraggedTrayCardId(null);

  if (over?.id !== "canvas") return;
  if (!active.rect.current.initial) return;

  setCards([
    ...cards,
    {
      id: active.id,
      coordinates: calculateCanvasPosition(
        active.rect.current.initial,
        over,
        delta
      ),
      text: active.id.toString(),
    },
  ]);
};
</code></pre>
<p>We add the new, additional, <code>DndContext</code>, a <code>div</code> to represent the tray, and a <code>DragOverlay</code>.</p>
<p>The <code>DragOverlay</code> component comes from DndKit, and we render the tray card being dragged inside it. It does the hard work of showing the tray card while it is being dragged. It is very convenient to use with Drag / Drop, but not as handy when just dragging, which is why we didn't use one earlier when dragging cards around the canvas.</p>
<pre><code class="lang-tsx">&lt;DndContext
  onDragStart={({ active }) =&gt; setDraggedTrayCardId(active.id)} 
  onDragEnd={addDraggedTrayCardToCanvas} 
&gt;
  &lt;div className="tray"&gt;
    {trayCards.map((trayCard) =&gt; {
      // this line removes the card from the tray if it's on the canvas
      if (cards.find((card) =&gt; card.id === trayCard.id)) return null;

      return &lt;Addable card={trayCard} key={trayCard.id} /&gt;;
    })}
  &lt;/div&gt;
  &lt;Canvas cards={cards} setCards={setCards} /&gt;
  &lt;DragOverlay&gt;
    {/* this works because the id of the card is the same as the text in this example so we can just render the id inside a div. In more complex cases you would have a component to render the card, and use that here. */}
    &lt;div className="trayOverlayCard"&gt;{draggedTrayCardId}&lt;/div&gt;
  &lt;/DragOverlay&gt;
&lt;/DndContext&gt;
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-2/blob/main/src/App.tsx">App.tsx on GitHub</a></p>
<h3 id="heading-addabletsx"><strong>Addable.tsx</strong></h3>
<p>The <code>Addable</code> component integrates with DndKit, and is used to represent cards that are not currently on the canvas, but can be dragged on to it.</p>
<p>We hook up the component with DndKit and render the card text in a <code>div</code>. <code>useDraggable</code> comes from DndKit, and we blindly pass the <code>attributes</code>, <code>listeners</code> and <code>setNodeRef</code> that it returns on to our <code>div</code>. This allows it to respond to <code>onClick</code> events and things like that.</p>
<pre><code class="lang-tsx">export const Addable = ({
   card
} : {
  card: Card;
}) =&gt; {
  const { attributes, listeners, setNodeRef } = useDraggable({
    card.id,
  });

  return (
    &lt;div 
      className="trayCard"
      ref={setNodeRef}
      {...listeners}
      {...attributes}&gt;
      {card.text}
    &lt;/div&gt;
  );
};
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-2/blob/main/src/Addable.tsx">Addable.tsx on GitHub</a></p>
<h3 id="heading-canvastsx-1"><strong>Canvas.tsx</strong></h3>
<p><code>Canvas</code> now needs to integrate with DndKit to make it a drop target, so that the <code>Addable</code> cards can be dropped on to it.</p>
<p>We hook up the canvas as a drop target with DndKit. <code>useDroppable</code> comes from DndKit, and we just pass the ref on to our <code>div</code>. This is so that DndKit can identify it, and obtain its <code>id</code> when it is dragged over.</p>
<pre><code class="lang-tsx">const { setNodeRef } = useDroppable({ id: "canvas" });
</code></pre>
<pre><code class="lang-tsx">&lt;div
  className="canvas"
  ref={setNodeRef}
  ...
&gt;
  ...
&lt;/div&gt;
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-2/blob/main/src/Canvas.tsx">Canvas.tsx on GitHub</a></p>
<h2 id="heading-step-3-how-to-pan-around-and-zoom-in-and-out-from-the-canvas"><strong>Step 3 - How to Pan Around and Zoom In and Out from the Canvas</strong></h2>
<p>In this final step, we'll install d3-zoom, hook it up to the canvas, and then update some calculations and styles so that everything appears in the right place.</p>
<p>We will update <code>App</code>, to store the <code>transform</code> (the pan and zoom of the canvas) from d3-zoom, and update the style of the <code>DragOverlay</code> and <code>calculateCanvasPosition</code> to take account of the <code>transform</code>.</p>
<p>We will update <code>Canvas</code> to integrate with d3-zoom and to use CSS styling to take account of the <code>transform</code>.</p>
<p>We will update <code>Draggable</code>, using CSS styling to take account of the <code>transform</code>, both when stationary and whilst being dragged.</p>
<p>d3-zoom handles mouse and pointer events for pan and zoom automatically, so we don't need to add any code for that (but it's easy to do so if you want to add a "Zoom In" button or similar).</p>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3">Step 3 code on GitHub</a></p>
<p>Here's what we'll build in this section, and there is also a <a target="_blank" href="https://ceddlyburge.github.io/react-figma-miro-canvas-part-3/">live demo</a> you can try out for yourself:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/part-3-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Before you continue on, make sure you've installed d3-zoom:</p>
<pre><code class="lang-bash">npm install d3-zoom
npm install --save-dev @types/d3-zoom
</code></pre>
<h3 id="heading-apptsx-2"><strong>App.tsx</strong></h3>
<p><code>App</code> now needs to store the <code>transform</code> (the pan and zoom of the canvas) from d3-zoom and update the style of the <code>DragOverlay</code> and <code>calculateCanvasPosition</code> to take account of the <code>transform</code>.</p>
<p>We store the current <code>transform</code> from d3-zoom. This represents both the pan (<code>transform.x</code> and <code>transform.y</code>) and the zoom (<code>transform.k</code>).</p>
<pre><code class="lang-tsx">const [transform, setTransform] = useState(zoomIdentity);
</code></pre>
<p>We add CSS to the <code>DragOverlay</code>, so that cards dragged from the tray are the same size that they are on the canvas.</p>
<pre><code class="lang-tsx">style={{
  transformOrigin: "top left",
  transform: `scale(${transform.k})`,
}}
</code></pre>
<p>We update the calculateCanvasPosition function as it now needs to account for the zoom of the canvas (<code>transform.k</code>) as well as the top left position.</p>
<pre><code class="lang-tsx">const calculateCanvasPosition = (
  initialRect: ClientRect,
  over: Over,
  delta: Translate,
  transform: ZoomTransform
): Coordinates =&gt; ({
  x: (initialRect.left + delta.x - (over?.rect?.left ?? 0) - transform.x) / transform.k,
  y: (initialRect.top + delta.y - (over?.rect?.top ?? 0) - transform.y) / transform.k,
});
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3/blob/main/src/App.tsx">App.tsx on GitHub</a></p>
<h3 id="heading-canvastsx-2"><strong>Canvas.tsx</strong></h3>
<p><code>Canvas</code> now needs to integrate with d3-zoom and to use CSS styling to take account of the <code>transform</code> from d3-zoom.</p>
<p>We add props for <code>transform</code> and <code>setTransform</code> (we pass these down from App.tsx).</p>
<pre><code class="lang-tsx">type Props = {
  cards: Card[];
  setCards: (cards: Card[]) =&gt; void;
  transform: ZoomTransform;
  setTransform(transform: ZoomTransform): void;
}
</code></pre>
<p>We hook up d3-zoom. Both DndKit and d3-zoom need a ref to the element, so we create <code>canvasRef</code> and <code>updateAndForwardRef</code>, which allows both of them to reference the same <code>HTMLDivElement</code>.</p>
<p>d3-zoom is a JavaScript library, rather than a React component, which is why we have to use the slightly esoteric code below, such as <code>useMemo</code> and <code>useLayoutEffect</code> (although you will see both of these in nearly any reasonably sized React codebase).</p>
<pre><code class="lang-tsx">const canvasRef = useRef&lt;HTMLDivElement | null&gt;(null);

const updateAndForwardRef = (div: HTMLDivElement) =&gt; {
  canvasRef.current = div;
  setNodeRef(div);
};

// create the d3 zoom object, and useMemo to retain it for rerenders
const zoomBehavior = useMemo(() =&gt; zoom&lt;HTMLDivElement, unknown&gt;(), []);

// update the transform when d3 zoom notifies of a change.
const updateTransform = useCallback(
  ({ transform }: { transform: ZoomTransform }) =&gt; {
    setTransform(transform);
  },
  [setTransform]
);

useLayoutEffect(() =&gt; {
  if (!canvasRef.current) return;

  // get transform change notifications from d3 zoom
  zoomBehavior.on("zoom", updateTransform);

  // attach d3 zoom to the canvas div element, which will handle
  // mousewheel, gesture and drag events automatically for pan / zoom
  select&lt;HTMLDivElement, unknown&gt;(canvasRef.current).call(zoomBehavior);
  },
  [zoomBehavior, canvasRef, updateTransform]
 );
</code></pre>
<p>We add a wrapper / window around the canvas. The canvas div will now pan and zoom (so will move around the screen a lot), so we wrap it in another div with a fixed position and size and hide any overflow, so that we have a "window" showing the relevant part of the canvas.</p>
<p>We also add CSS styles to the canvas to account for the pan and zoom, use the new <code>updateAndForwardRef</code> function and move the <code>ref</code> from the canvas to the canvas window.</p>
<pre><code class="lang-tsx">&lt;div ref={updateAndForwardRef} className="canvasWindow"&gt;
  &lt;div
    className="canvas"
    style={{
      // apply the transform from d3
      transformOrigin: "top left",
      transform: `translate3d(${transform.x}px, ${transform.y}px, ${transform.k}px)`,
      position: "relative",
      height: "300px",
    }}
  &gt;
    ...
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3/blob/main/src/Canvas.tsx">Canvas.tsx on GitHub</a></p>
<h3 id="heading-draggabletsx-1"><strong>Draggable.tsx</strong></h3>
<p><code>Draggable</code> now needs different CSS styling to take account of the d3-zoom <code>transform</code>, both when stationary and whilst being dragged.</p>
<p>We add a prop for the d3-zoom transform, which we call <code>canvasTransform</code>, as we are already using a <code>transform</code> variable from DndKit.</p>
<pre><code class="lang-tsx">type Props = {
  card: Card;
  canvasTransform: ZoomTransform;
}
</code></pre>
<p>We update the CSS to account for the canvas pan and zoom. We have to handle two cases, both when it is currently being dragged, and when it is not.</p>
<pre><code class="lang-tsx">style={{
  position: "absolute",
  top: `${card.coordinates.y * canvasTransform.k}px`,
  left: `${card.coordinates.x * canvasTransform.k}px`,
  transformOrigin: "top left",
  ...(transform
    ? {
        // temporary change to this position when dragging
        transform: `translate3d(${transform.x}px, ${transform.y}px, 0px) scale(${canvasTransform.k})`,
      }
    : {
        // zoom to canvas zoom
        transform: `scale(${canvasTransform.k})`,
      }),
}}
</code></pre>
<p>We also stop the <code>onPointerDown</code> event bubbling up to the canvas, otherwise it would be handled by d3-zoom, and interpreted as a request to start dragging, which results in dragging the canvas and the card at the same time (an interesting but undesirable effect!)</p>
<pre><code class="lang-tsx">onPointerDown={(e) =&gt; {
  listeners?.onPointerDown?.(e);
  e.preventDefault();
}}
</code></pre>
<p><a target="_blank" href="https://github.com/ceddlyburge/react-figma-miro-canvas-part-3/blob/main/src/Draggable.tsx">Draggable.tsx on GitHub</a></p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>There is some complexity to the various position / transform calculations, but it isn't too crazy, and there are only two dependencies to install.</p>
<p>There are only four components (<code>App</code>, <code>Canvas</code>, <code>Draggable</code> and <code>Addable</code>), and only about 250 lines of code for all of them, which seems like a very modest amount for all the functionality.</p>
<p>This demo is very simple, but it contains a lot of virtual canvas functionality, and it is easy to use this as a base and build something more elaborate on top.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Open-Closed Principle – The Software Development Concept Explained in Plain English ]]>
                </title>
                <description>
                    <![CDATA[ There are many articles about the Open-Closed Principle, but I can never find one that explains it in a way that really works for me.  So here, hopefully, is a good one – with a non trivial and real life example, what changes to support, and a descri... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/open-closed-principle/</link>
                <guid isPermaLink="false">66bb926e0eaca026d8cfa5f4</guid>
                
                    <category>
                        <![CDATA[ software design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Mon, 27 Sep 2021 19:43:06 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/09/IMG_8905.JPG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>There are many articles about the Open-Closed Principle, but I can never find one that explains it in a way that really works for me. </p>
<p>So here, hopefully, is a good one – with a non trivial and real life example, what changes to support, and a description of the trade offs.</p>
<p>The Open-Closed principle states that code should be "Open for extension" and "Closed for modification". </p>
<p>There is <a target="_blank" href="https://codeblog.jonskeet.uk/2013/03/15/the-open-closed-principle-in-review/">quite a lot of confusion about the term</a>, but essentially it means that if you want to implement a <em>supported</em> change, you should be able to do it without changing the code in a large number of places. Ideally, you can implement the new feature just by adding new code, and changing little or no old code, which makes the code easier to develop and maintain. </p>
<p>Open is the 'O' in the <a target="_blank" href="https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/">SOLID design principles</a>, which are probably the most famous guides for writing high quality code.</p>
<p>No useful code can ever be completely open to all possible changes, so we have to decide which changes we are going to <em>support</em>. When writing our code we can think about what the potential changes might be, decide which ones to <em>support</em>, and then make the code 'open' to these. </p>
<p>We can create a list of these potential changes by:</p>
<ul>
<li>Analysing the code</li>
<li>Looking at previous changes to the code</li>
<li>Using our experience of commonly requested changes</li>
<li>Using any knowledge of upcoming feature requests</li>
</ul>
<p>Take a minute to look at the code below (<a target="_blank" href="https://github.com/ceddlyburge/open-closed-principle/blob/master/OpenClosedPrinciple/Original/GrossToNetCalculator.cs">and on GitHub</a>) and think about what changes we might expect. You don't have access to the commit history, or any knowledge of upcoming feature requests, but you can still probably come up with some likely candidates.</p>
<pre><code class="lang-csharp=">public class GrossToNetCalculator
{
    public GrossToNetCalculator(
        IGrossEnergyYield grossYield,
        double grossEnergy,
        double hysteresisLoss,
        double curtailmentLossGrid,
        double turbineLossTurbulence,
        double electricalLoss,
        double turbineLossShear,
        double turbinePerformanceExperience,
        double operationalExperienceLoss)
    {
        double dependentLoss = 
            CombinePercentages(
                grossYield.TurbineAvailability,
                grossYield.BalanceAvailability,
                grossYield.AccessibilityAvailability,
                hysteresisLoss,
                electricalLoss,
                grossYield.EnvironmentalShutdownWeather,
                grossYield.EnvironmentalSiteAccess,
                grossYield.EnvironmentTreeGrowth);

        double independentLoss = 
            CombinePercentages(
                grossYield.GridAvailability,
                turbinePerformanceExperience,
                turbineLossTurbulence,
                grossYield.EnvironmentalPerformanceDegradationIcing,
                grossYield.CurtailmentPowerPurchase,
                grossYield.SubOptimalPerformance,
                turbineLossShear,
                operationalExperienceLoss);

        GrossToNet = 
            1 - 
            (1 - (dependentLoss + curtailmentLossGrid))
            * (1 - independentLoss);
    }

    double CombinePercentages(params double[] percentages)
    {
        double combination = 1;
        foreach (var percentage in percentages)
            combination *= 1 - percentage;
        return 1 - combination;
    }

    public double GrossToNet { get; private set; }
}
</code></pre>
<p>This code is relatively simple, and when I look at it these are the potential changes that I see:</p>
<ul>
<li>Items could be added or removed from the <code>dependentLoss</code> and <code>independentLoss</code> calculations. Items could be also be moved between <code>dependentLoss</code> and <code>independentLoss</code>, but this is essentially the same thing</li>
<li>The calculation of <code>GrossToNet</code> could change</li>
<li>The <code>CombinePercentages</code> calculation could change</li>
</ul>
<p>As with most things in computer programming, there is a tension when applying the Open-Closed Principle. </p>
<p>On the one hand, making the code more easily extensible is good. On the other hand, doing this often breaks encapsulation, adds complication, and adds unnecessary levels of abstraction. </p>
<p>So again, we need to make a decision about which of these changes we want to support and make the code 'open to'<em>.</em> We can then avoid adding unnecessary complication to the code for unsuitable changes. </p>
<p>It is worth remembering that the work can always be done later, when it will be easier, as we will know exactly what is required.</p>
<p>To make a decision about what changes we should support and make the code 'open to',  we need to estimate how likely the change is to occur, think about design solutions, and then think about the trade offs.</p>
<h2 id="heading-we-could-add-or-remove-items-from-the-dependentloss-and-independentloss-calculations">We Could Add or Remove Items from the dependentLoss and independentLoss Calculations</h2>
<h3 id="heading-ia"> </h3>
<p>Very likely to change</p>
<p>The calculation of <code>dependentLoss</code> and <code>independentLoss</code> (for example <code>double dependentLoss = CombinePercentages(...)</code>) each use 8 parameters (<code>electricalLoss</code>, <code>TurbineAvailability</code> and so on).</p>
<p>These 16 make up the majority of the 17 total inputs to the entire calculation. So, from a purely statistical point of view, a change to one of these has a 16/17 (94%) chance of affecting these calculations.</p>
<p>It's also easy to imagine that we might want to add another "Loss" or "Availability" or similar in the future, or that a current one is no longer relevant, or that different combinations will be required in different circumstances.</p>
<h3 id="heading-possible-solution">Possible solution</h3>
<p>Take a list of dependent and independent losses in the constructor, instead of taking each loss individually. So the existing constructor:</p>
<pre><code class="lang-csharp=">public GrossToNetCalculator(
    ...
    double hysteresisLoss,
    double curtailmentLossGrid,
    ...)
</code></pre>
<p>is replaced with this:</p>
<pre><code class="lang-csharp=">public GrossToNetCalculator(
    IReadOnlyList&lt;double&gt; dependentLosses
    IReadOnlyList&lt;double&gt; independentLosses)
</code></pre>
<p>This means that if the change is requested, we can implement it without changing the class (and instead just changing the parameters we pass to the constructor). </p>
<p>For example, if another 'dependentLoss' is requested, we can just add this to the <code>dependentLosses</code> list.</p>
<p>(You can see the <a target="_blank" href="https://github.com/ceddlyburge/open-closed-principle/tree/master/OpenClosedPrinciple/ListParameters">code on GitHub here</a>)</p>
<h3 id="heading-trade-offs">Trade offs</h3>
<p>A small amount of encapsulation is lost, and the calling code would now be in charge of deciding which losses to pass in.</p>
<p>The code adheres much better to the Open-Closed Principle and becomes much more easily extendable and reusable. If you need to make a change, you won't need to modify the tests, which is useful as they are complicated. </p>
<p>Tests for the calling code would have to modified, but only to verify that they pass the correct parameters, which is much simpler. </p>
<p>It is possible that the constructor parameters are passed around in the code base, and now there are only two parameters, as opposed to the previous nine.</p>
<h3 id="heading-decision">Decision</h3>
<p>We should implement this solution to support this change and make the code 'open' to it. </p>
<h2 id="heading-the-grosstonet-calculation-could-change">The GrossToNet Calculation Could Change</h2>
<h3 id="heading-ia-1"> </h3>
<p>Unlikely to change</p>
<p>The GrossToNet calculation is <code>GrossToNet = 1 - (1 - (dependentLoss + curtailmentLossGrid)) * (1 - independentLoss);</code></p>
<p>Only the <code>curtailmentLossGrid</code> parameter is used, aside from the <code>dependentLoss</code> and <code>independentLoss</code> which are covered earlier.</p>
<p>This 1 parameter is a small minority of the 17 total inputs to the entire calculation. So, from a purely statistical point of view, a change to one of these has a 1/17 (6%) chance of affecting this calculation.</p>
<h3 id="heading-possible-solutions">Possible solutions</h3>
<ol>
<li>Take a lambda parameter in the constructor to calculate <code>GrossToNet</code> and pass it <code>dependentLoss</code> and <code>independentLoss</code>, so that the calculation becomes <code>GrossToNet = grossToNetCalculatorLambda(dependentLoss, independentLoss)</code>(<a target="_blank" href="https://github.com/ceddlyburge/open-closed-principle/tree/master/OpenClosedPrinciple/GrossToNetLambda">code on GitHub</a>)</li>
<li>Remove <code>curtailmentLossGrid</code> from the calculation, which then becomes completely generic and can be renamed to <code>PercentageCombiner</code>. Require that the calling code applies this adjustment (this adjustment is too complicated for useful example code)</li>
<li>Remove <code>curtailmentLossGrid</code> from the calculation as above, then recreate the original <code>GrossToNetCalculator</code>, using the <code>PercentageCombiner</code> and adding <code>curtailmentLossGrid</code> to the calculation<br>(<a target="_blank" href="https://github.com/ceddlyburge/open-closed-principle/tree/master/OpenClosedPrinciple/PercentageCombiner">code on GitHub</a>)</li>
</ol>
<h3 id="heading-trade-offs-1">Trade Offs</h3>
<p>A large amount of encapsulation is lost for options 1 and 2. Option 3 is a reasonable amount of work, and adds a layer of abstraction.</p>
<h3 id="heading-decision-1">Decision</h3>
<p>This change isn't likely to happen, so it probably isn't worth the effort involved to support it and make the code 'open' to it. But if we had another use for the new <code>PercentageCombiner</code> then it would definitely be worthwhile.</p>
<h2 id="heading-the-combinepercentages-calculation-could-change">The CombinePercentages Calculation Could Change</h2>
<h3 id="heading-ia-2"> </h3>
<p>Very unlikely to change</p>
<pre><code class="lang-csharp=">CombinePercentages(params double[] percentages)
{
    double combination = 1;
    foreach (var percentage in percentages)
    combination *= 1 - percentage;
    return 1 - combination;
}
</code></pre>
<p>The CombinePercentages calculation implements some standard laws of math / statistics, which do not change.</p>
<h3 id="heading-possible-solutions-1">Possible solutions</h3>
<ol>
<li>Take a lambda parameter in the constructor to combine the percentages, and use this instead of the CombinePercentages function. So instead of having <code>double dependentLoss = CombinePercentages(...)</code>, you would have  <code>double dependentLoss = combinePercentagesLambda(...)</code>.<br>(<a target="_blank" href="https://github.com/ceddlyburge/open-closed-principle/tree/master/OpenClosedPrinciple/CombinePercentagesLambda">code on GitHub</a>)</li>
<li>Create a <code>PercentageCombiner</code> abstraction, take this in the constructor to combine the percentages, and use this instead of the CombinePercentages function. So instead of having <code>double dependentLoss = CombinePercentages(...)</code>, you would have <code>double dependentLoss = percentageCombiner.CombinePercentages(...)</code>.<br>(<a target="_blank" href="https://github.com/ceddlyburge/open-closed-principle/tree/master/OpenClosedPrinciple/PercentageCombinerAbstraction">code on GitHub</a>)</li>
</ol>
<h3 id="heading-trade-offs-2">Trade offs</h3>
<p>Combining percentages is at the heart of what this code does, so removing this logic makes the code mostly useless. </p>
<p>Option 1 passes all the responsibility for this on to the caller, whereas option 2 at least allows for predefined implementations of the abstraction.</p>
<h3 id="heading-decision-2">Decision</h3>
<p>This change is very unlikely, and the only reasonable solution (option 2) requires a lot of work and adds complexity and abstraction. </p>
<p>This means that it would only make sense to do it when the change is actually required, and even then only if multiple algorithms are required. Note that if a change is required to the algorithm, it will make more sense to simply change the implementation of the CombinePercentages function.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Deciding whether code adheres to the Open-Closed Principle is almost always a judgement call, and there are usually trade offs involved with encapsulation, complexity and abstraction. </p>
<p>It is worth thinking about likely changes and extensions, and using these to guide your decisions.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Make Calculation Tests Simpler and More Expressive with These New Refactorings ]]>
                </title>
                <description>
                    <![CDATA[ It is a good idea to make tests as descriptive as possible – to achieve Tests as Documentation. Including the calculations in the test is a big part of this, and avoids the Hard Coded Test Data smell. For example, if a test looks like this, it is har... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-test-complicated-calculations-new-refactoring/</link>
                <guid isPermaLink="false">66bb9264fce17a7d99885305</guid>
                
                    <category>
                        <![CDATA[ refactoring ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Wed, 03 Feb 2021 17:32:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/02/unit-testing-calculations.JPG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>It is a good idea to make tests as descriptive as possible – to achieve <a target="_blank" href="http://xunitpatterns.com/Goals%20of%20Test%20Automation.html#Tests%20as%20Documentation">Tests as Documentation</a>. Including the calculations in the test is a big part of this, and avoids the <a target="_blank" href="http://xunitpatterns.com/Obscure%20Test.html#Hard-Coded%20Test%20Data">Hard Coded Test Data</a> smell.</p>
<p>For example, if a test looks like this, it is hard to know what the problem is when it fails, as the test data is all hard coded. The test tells you very little about how the system under test (SUT) should behave, and so doesn’t act as documentation.</p>
<pre><code class="lang-python"><span class="hljs-keyword">assert</span> sut.wake_erosion_rate(<span class="hljs-number">0.03</span>) == <span class="hljs-number">0.8</span>
</code></pre>
<p>However, if you remove this code smell, and include the calculation in the test, it becomes obvious what the problem is when there is a failure, and the test does now act as documentation. </p>
<p>You might also want to replace the <code>2.5</code> and <code>0.05</code> with the names of the constants they represent.</p>
<pre><code class="lang-python">ambient_turbuluence = <span class="hljs-number">0.03</span>

<span class="hljs-keyword">assert</span> 
    sut.wake_erosion_rate(ambient_turbuluence) 
    == <span class="hljs-number">2.5</span> * ambient_turbuluence + <span class="hljs-number">0.05</span>
</code></pre>
<p>This article references the excellent <a target="_blank" href="https://www.goodreads.com/review/show/2179089513">XUnit Test Patterns book</a>, which defines the most widely accepted lexicon on unit test patterns and practices.</p>
<h2 id="heading-example-calculation">Example Calculation</h2>
<p>The rest of this article will discuss the <a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/cash_flow_calculator/construction_margin_calculator.py#L31">example ConstructionMarginCalculator class</a>, and will describe refactorings that simplify the tests and allow them to feasibly include the calculations. </p>
<p>The calculation is around 30 lines long, and is worth a quick glance now if you have the time. But if not, don’t worry, the rest of this post will still make sense.</p>
<p>There are a few if statements and a loop, and in total these lead to 2^9, or 512, paths through the code! Eek! It is clearly not feasible (or useful) to test all these, hence the need to find ways to make it easier.</p>
<p>This example code has an <a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/tests/test_construction_margin_calculator.py#L17">initial naive test</a>, which is around 30 lines long. It doesn’t include the calculations, and introducing them would make the test even longer and more complicated, so would be hard to justify.</p>
<p>Each of the following sections describes a refactoring, and includes links to the refactored example code. These build on the existing <a target="_blank" href="http://xunitpatterns.com/Test%20Refactorings.html">test refactorings</a> from <a target="_blank" href="https://www.goodreads.com/review/show/2179089513">XUnit Test Patterns</a>.</p>
<p>The refactorings reduce the number of paths through the code and simplify the test. This means that fewer tests are needed, and that the tests become small and simple enough to include the calculations.</p>
<h2 id="heading-extract-calculation-from-loop">Extract Calculation From Loop</h2>
<p>The code initially calculates values for a <em>list of</em> <code>steps</code>, which means that any test for it must take on this complication. </p>
<p>The setup of the test is harder, as we have to create a list as opposed to a single value. The assertions are harder as we have to assert on a list as opposed to a single value. Finally, we should have multiple tests (probably for 0, 1, and multiple items in the list).</p>
<p>The easy fix is simply to refactor the code so that it only calculates a single <code>step</code>. This displaces the iteration code to some other place, which might want testing. However, this can be tested using a Mock, so only the iteration needs to be tested (as opposed to the iteration <em>and</em> the calculation), which is simple, and potentially so simple that you don’t need to test it.</p>
<p>This refactoring means that instead of requiring 3 tests (for 0, 1, and multiple items in the list), there is now just one. This reduces the number of paths through the code from 2^9 to 2^7, or 128. This is already a lot better, but still too many to test!</p>
<ul>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/cash_flow_calculator/construction_margin_calculator_without_loop.py#L31">Refactored Calculation</a></li>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/tests/test_construction_margin_calculator_remove_loop.py#L11">Refactored Test</a></li>
</ul>
<h2 id="heading-introduce-mockable-abstractions">Introduce Mockable Abstractions</h2>
<p>The details of the inflation calculation aren’t shown in the example, but they are reasonably complicated.</p>
<p>To avoid this we can change the code to accept an <code>InflationCalculator</code>. The inflation calculation always uses the same <code>date_of_financial_close</code>, <code>inflation_rate</code> and <code>inflation_mode</code>, which means that it can take these in the constructor. This in turn means that the main calculation no longer requires the <code>inflation_rate</code> and <code>inflation_mode</code>.</p>
<p>Then in the tests we can create a mock <code>InflationCalculator</code>. This could for example always return a value of <code>2</code>, which makes the overall calculation a lot simpler.</p>
<p>It is also easy to imagine that inflation calculations will happen in other parts of the code, so the abstraction will be widely useful.</p>
<p>This step means that instead of 4 conditional branches in the inflation calculation, there is now just one. This reduces the number of paths through the code from 2^7 to 2^3, or just 8. It is also necessary to test the <code>InflationCalculator</code>, and this has 4 branches, so needs 4 tests, but this still only makes 12 tests in total. Yay for loosely coupled code!</p>
<p>We now have a feasible number of tests to write, but including the calculation in the test is still going to be very cumbersome. Luckily we still have some refactorings left up our sleeve.</p>
<ul>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/cash_flow_calculator/construction_margin_calculator_mockable_abstraction.py#L29">Refactored Calculation</a></li>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/tests/test_construction_margin_calculator_mockable_abstraction.py#L11">Refactored Test</a></li>
</ul>
<h2 id="heading-test-conditional-branches-in-isolation">Test Conditional Branches in Isolation</h2>
<p>The code branches based on certain conditionals. We can simply make some of these conditionals <code>False</code>, and then test each branch of the code in isolation. This way, each test only has to include the calculation for the branch that it is concerned with.</p>
<p>For example, we can set <code>in_selling_mode</code> to be <code>False</code> and <code>step.start_of_step</code> to be different to <code>date_of_financial_close</code>. This makes the test simple enough that it is feasible for it to perform the same calculation that the code does. </p>
<p>This in turn means that the test clearly communicates that the <code>turbine_cost_including_margin</code> should be the <code>turbine_costs * fraction_of_spend * inflation</code>. This helps readers understand the calculator, and achieves the goal of <a target="_blank" href="http://xunitpatterns.com/Goals%20of%20Test%20Automation.html#Tests%20as%20Documentation">Tests as Documentation</a></p>
<p>At the moment the test is still quite long. However, because we are only testing a small part of the calculation, a lot of this information is now irrelevant (the <code>any_double</code> variable). This means we can now create a <a target="_blank" href="http://natpryce.com/articles/000714.html">Test Data Builder</a>, or use helper functions to make things more concise. We will see an example of this in the next refactoring.</p>
<ul>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/cash_flow_calculator/construction_margin_calculator_mockable_abstraction.py#L29">No Change to Calculation</a></li>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/tests/test_construction_margin_calculator_isolate_branches.py#L16">Refactored Test</a></li>
</ul>
<h2 id="heading-test-values-in-isolation">Test Values in Isolation</h2>
<p>“Test Conditional Branches in Isolation” is a useful technique, but it can still leave us with some complications if a value is calculated / updated in multiple branches. </p>
<p>A good example of this is <code>balance_of_plant_cost_including_margin</code>, which is set initially, and then updated in the <code>if (in_selling_mode)</code> branch.</p>
<p>Testing <code>balance_of_plant_cost_including_margin</code> in isolation allows the test to concentrate just on this one value / calculation, which means that a lot less setup is required. The <a target="_blank" href="http://natpryce.com/articles/000714.html">Test Data Builder</a> pattern hides <a target="_blank" href="http://xunitpatterns.com/Obscure%20Test.html#Irrelevant%20Information">Irrelevant Information</a>, and the test becomes more concise and expressive.</p>
<p>Including the calculation continues to make the test clearly communicate its intent and act as documentation. Interestingly the test calculation code is no longer an exact copy of the SUT calculation code, as it already knows that it is <code>in_selling_mode</code>, so doesn’t need a conditional statement. This means that the test is simpler than the code, which helps avoid the <a target="_blank" href="http://xunitpatterns.com/Obscure%20Test.html">Obscure Test</a> smell.</p>
<ul>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/cash_flow_calculator/construction_margin_calculator_mockable_abstraction.py#L29">No Change to Calculation</a></li>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/tests/test_construction_margin_calculator_isolate_values.py#L18">Refactored Test</a></li>
</ul>
<h2 id="heading-test-partial-values-in-isolation">Test Partial Values in Isolation</h2>
<p>Sometimes the calculation of individual values is very complex, and can’t be split up by conditional branches. This can make it challenging to include the calculation in the test. <code>construction_profit</code> is a reasonable example of this, which is calculated as follows:</p>
<pre><code class="lang-python">step.turbine_cost_including_margin = 
 turbine_costs * inflation * fraction_of_spend;

step.balance_of_plant_cost_including_margin = 
 balance_of_plant_costs_at_financial_close * inflation * fraction_of_spend;

step.construction_profit = 
 <span class="hljs-number">-1</span> * 
 (step.turbine_cost_including_margin + step.balance_of_plant_cost_including_margin) *
 epc_margin
</code></pre>
<p><code>inflation</code>, <code>fraction_of_spend</code> and <code>epc_margin</code> have a multiplicative effect, so if we set them to <code>1</code>, they won’t have any effect and we can easily write a test for the rest of the logic.</p>
<p><code>step.turbine_cost_including_margin</code> and <code>step.balance_of_plant_cost_including_margin</code> have an additive effect, so if we set them to <code>0</code>, again they won’t have any effect and we can easily write a test for the rest of the logic.</p>
<p>Testing just a portion of <code>construction_profit</code> in isolation allows the test to concentrate just on this part of the calculation, which, as before, makes the test shorter and simpler, and avoids the <a target="_blank" href="https://hackmd.io/4xuiUbrAQimsWCknJiqXNw?both">Obscure Test</a> smell.</p>
<ul>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/cash_flow_calculator/construction_margin_calculator_mockable_abstraction.py#L29">No Change to Calculation</a></li>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/tests/test_construction_margin_calculator_isolate_partial_values.py#L14">Refactored Test</a></li>
</ul>
<h2 id="heading-introduce-the-blackboard-pattern">Introduce the Blackboard Pattern</h2>
<p>The Blackboard pattern is a more involved technique, and is often badly understood. But essentially it involves using a “blackboard” to break the dependencies within complicated calculations.</p>
<p>In this example the <code>construction_profit</code> depends on <code>turbine_cost_including_margin</code> and <code>balance_of_plant_cost_including_margin</code>, which are themselves calculated from the inputs. This makes testing harder. </p>
<p>In order to test the <code>construction_profit</code> you essentially also have to test <code>turbine_cost_including_margin</code> and <code>balance_of_plant_cost_including_margin</code>.</p>
<p>When we introduce the blackboard pattern, one calculator writes the <code>turbine_cost_including_margin</code> and <code>balance_of_plant_cost_including_margin</code> to the blackboard, and when calculating the <code>construction_profit</code> we read these values from the blackboard.</p>
<p>This breaks the connection between the two things, so when testing we can just add values for <code>turbine_cost_including_margin</code> and <code>balance_of_plant_cost_including_margin</code> to the blackboard.</p>
<ul>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/cash_flow_calculator/construction_margin_calculator_blackboard_pattern.py#L15">Refactored Calculation</a></li>
<li><a target="_blank" href="https://github.com/ceddlyburge/unit-testing-calculations/blob/main/tests/test_construction_margin_calculator_blackboard_pattern.py#L12">Refactored Test</a></li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When testing calculations, it is important to include the calculation in the tests. This avoids the <a target="_blank" href="http://xunitpatterns.com/Obscure%20Test.html#Hard-Coded%20Test%20Data">Hard Coded Test Data</a> smell, allows the tests to clearly express their intent and achieve <a target="_blank" href="http://xunitpatterns.com/Goals%20of%20Test%20Automation.html#Tests%20as%20Documentation">Tests as Documentation</a>.</p>
<p>The refactorings described in this article allow you to do this while also making sure that the tests are concise and understandable.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Big O Notation Works – Explained with Cake ]]>
                </title>
                <description>
                    <![CDATA[ Big O notation is used in computer science to define an upper bound of an algorithm. It is mostly used to define the maximum time of an algorithm as a function of the input size, but it can also be used to define memory usage. In this article we will... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/big-o-notation/</link>
                <guid isPermaLink="false">66bb9258867a396452a80284</guid>
                
                    <category>
                        <![CDATA[ algorithms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #big o notation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Mon, 28 Dec 2020 14:54:48 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5fd9038ee6787e098393f598.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Big O notation is used in computer science to define an upper bound of an algorithm. It is mostly used to define the maximum time of an algorithm as a function of the input size, but it can also be used to define memory usage.</p>
<p>In this article we will talk through the most common types of ‘Big O’ notation, using birthday cakes to illustrate the concepts. We'll pretend we're hosting a party, and need to determine how many cakes to bake based on how many people attend.</p>
<h2 id="heading-o1-constant-time">O(1) - Constant Time</h2>
<p>For the Constant Time example, no matter how many people come to the birthday party, you only make one cake. So the cake making time stays constant.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-1--constant-time.png" alt="0(1) Constant Time Illustration" width="600" height="400" loading="lazy"></p>
<p>Note that Big O notation doesn’t specify how long the Constant Time is (maybe it takes 1 hour to make the cake, maybe it takes 4 hours). It just states that the time taken doesn’t increase with the number of guests.</p>
<p>A real world example of an O(1) operation is accessing an array by its index. It is just as quick to retrieve an element from a 10 item array as it is to retrieve an element from a 1 million item array.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-1--constant-time-grqph.png" alt="0(1) Constant Time Graph" width="600" height="400" loading="lazy"></p>
<h2 id="heading-olog-n-logarithmic-time">O(log n) - Logarithmic Time</h2>
<p>For the Logarithmic Time example, the birthday cakes are used as a way to incentivise people to turn up to the party on time. </p>
<p>The first person to arrive gets a cake all to themselves. Then the next 2 people to arrive share a cake. Then the next 4 people all share a cake, and so on.</p>
<p>So a 1 person party requires 1 cake. A 2 or 3 person party requires 2 cakes. A 4 - 7 person party requires 3 cakes, and a 8 to 15 person party requires 4 cakes. In general an ‘n’ person party requires log<em>2</em>(n) cakes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-log-n--logarithmic-time.png" alt="O(log n) Logarithmic Time Illustration" width="600" height="400" loading="lazy"></p>
<p>The most common real world example of an O(log n) operation is a binary search of an ordered array. </p>
<p>This algorithm looks at the middle of an array and sees if the value is lower or higher than the one it is looking for. Since the list is ordered, it then knows which half of the array the target value is in. </p>
<p>It then repeats the process with that half of the array. So for a 16 item array, the first iteration narrows down the search to 8 items, then 4 then 2 and then 1, for a maximum of 4, or log<em>2</em>(16), iterations over all.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-log-n--logarithmic-time-graph.png" alt="O(log n) Logarithmic Time Graph" width="600" height="400" loading="lazy"></p>
<h2 id="heading-on-linear-time">O(n) - Linear Time</h2>
<p>For the Linear Time example, each guest gets their own cake. If ‘n’ people come to the party, you need to make ‘n’ cakes. So the time taken is related to the number of guests. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-n--linear-time.png" alt="O(n) Linear Time Illustration" width="600" height="400" loading="lazy"></p>
<p>Again Big O notation doesn’t specify how long the time is (maybe it takes 1 hour to make the cake, maybe it takes 4 hours), it just states that the time increases linearly with the number of guests.</p>
<p>A real world example of an O(n) operation is a naive search for an item in an array. In a 10 item array, at worst you will have to look all 10 items to find the one you want. But for a 1 million item array, you may have to look at all 1 million. </p>
<p>Of course, you might find the solution sooner, but Big O notation specifies the maximum amount of time that an algorithm will take.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-n--linear-time-graph.png" alt="O(n) Linear Time Graph" width="600" height="400" loading="lazy"></p>
<h2 id="heading-on2-quadratic-time">O(n^2) - Quadratic Time</h2>
<p>For the Quadratic Time example, each guest gets their own cake. Additionally, each cake has the names of all the guests written on it, with some delicious icing. </p>
<p>In this case a 1 person party has one cake with one name on it. A 2 person party has two cakes, both with two names on (4 names total) and a 3 person party has three cakes, all with three names on them, which is 9 names in total.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-n-2--quadratic-time.png" alt="O(n^2) Quadratic Time Illustration" width="600" height="400" loading="lazy"></p>
<p>In general, an ‘n’ person party requires n*n names to be written (also known as n squared, or n to the power of 2), so the speed of making cakes (and writing all the names) is related to the square of the number of guests.</p>
<p>A real world example of an O(n^2) operation is a naive search for duplicates in an array. In this scenario, you loop through all the items in the array, and for each of those items, loop through the array again to see if there are any matches. </p>
<p>For a 10 item array, the outer loop has 10 iterations, and for each of those there are 10 iterations of the inner loop, for 100 in total. For a 1 million item array, it is 1000 billion.</p>
<p>There is a more general case of O(n^2), where instead of the time being relative to n to the power of 2 (n^2), it is relative to n to the power of c (n^c). This is usually called Polynomial Time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-n-2--quadratic-time-graph.png" alt="O(n^2) Quadratic Time Graph" width="600" height="400" loading="lazy"></p>
<h2 id="heading-on-factorial-time">O(n!) - Factorial Time</h2>
<p>For the Factorial Time example, the guests take part in a <a target="_blank" href="https://en.wikipedia.org/wiki/P%C3%A9tanque">Pétanque</a> competition, and the winner takes home the cake. </p>
<p>There is a slight issue, though, in that the player that takes the first turn is at a disadvantage. To help level things out, many games are played, so that each permutation of guests is covered and everyone gets to go first. All these permutations are written on to the cake, again with some tasty icing.</p>
<p>This means that a 2 person party has two games, as each guest takes it in turn to go first. A 3 person party has 6 games (if we imagine that the guests are Anna, Brian and Chris, then the permutations are ABC, ACB, BAC, BCA, CAB, CBA).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/o-n---factorial-time.png" alt="O(n!) - Factorial Time Illustration" width="600" height="400" loading="lazy"></p>
<p>In general, an ‘n’ person party requires n!, or n factorial games, so the speed of making the cake is related to the this. </p>
<p>n! is calculated by multiplying all whole numbers from n down to one “n <em> (n - 1)  </em> (n - 2) … <em> 2 </em> 1”. So for the 2 person party it is 2 <em> 1, or 2. For the three person party it is 3 </em> 2 * 1, which is 6.</p>
<p>Real world examples of O(n!) operations are anything that requires analysing a list of permutations, such as the <a target="_blank" href="https://en.wikipedia.org/wiki/Travelling_salesman_problem">traveling salesman problem</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-165.png" alt="O(n!) Factorial Time Graph" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusions">Conclusions</h2>
<p>Hopefully the birthday cakes have made ‘Big O’ notation easier to digest! The graph below is also a good memory aid, showing the relative speeds of the algorithms (if there is a choice, then you want the faster one!)</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-166.png" alt="Big O Notation graph" width="600" height="400" loading="lazy"></p>
<p>There are quite a few other ‘Big O’ notations, such as O(n log n) and O(c^n) but they all follow the same pattern. If you want to learn more about it, <a target="_blank" href="https://www.freecodecamp.org/news/big-o-notation-why-it-matters-and-why-it-doesnt-1674cfa8a23c/">check out this article</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Documentation from Your Python Tests ]]>
                </title>
                <description>
                    <![CDATA[ What if I told you that you could automatically create documentation from your existing tests that would always be up to date?  And what if it could be in markdown format, so it would be committed along with the rest of your code, and be shown on Git... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-documentation-from-your-python-tests/</link>
                <guid isPermaLink="false">66bb9260add24ba427325109</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Tue, 15 Sep 2020 19:12:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/09/docs-from_tests_example_graph.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>What if I told you that you could automatically create documentation from your existing tests that would always be up to date? </p>
<p>And what if it could be in markdown format, so it would be committed along with the rest of your code, and be shown on GitLab / GitHub?</p>
<p>Sounds pretty cool, right? Let's see how it's done.</p>
<h3 id="heading-context">Context</h3>
<p>People like <a target="_blank" href="https://c4model.com/">Simon Brown</a> do a great job of convincing me that I don't have enough documentation for my projects. And that the documentation should be up to date, and show concise information at a variety of levels of abstraction. </p>
<p>I would love to work on a code base with documentation like that.</p>
<h2 id="heading-the-problem-with-documentation">The Problem with Documentation</h2>
<p>I have read a good number of <a target="_blank" href="https://www.goodreads.com/book/show/44144493-fundamentals-of-software-architecture">books</a> and articles about software architecture and related things. But I have never been able to summon up enough energy, or enough political capital, to be able to create documentation to this standard. Let alone keep it up to date.</p>
<p>So, for my situation at least, I need a way of creating and updating documentation automatically. </p>
<p>I would also like to store the diagrams "as code", so that they can be checked in to the repository. This way, changes to them can be easily seen and discussed in pull requests and other code reviews.</p>
<p>There are many <a target="_blank" href="https://structurizr.com/help/code">tools</a> that can generate build time dependency diagrams from code, and I have used quite a few of them. </p>
<p>But the problem seems to be that these diagrams always look like spaghetti, even when the code is good. And they are complex to set up. </p>
<p>It seems to be very hard to get the level of detail right. There is no way to show related code in logical groups for high level diagrams. There is also no way to pick out code relationships that are specific to a particular context for low level diagrams. </p>
<p>They also give you no information about the run time relationships of the code, which is usually a bigger issue than the design time relationships.</p>
<h2 id="heading-a-solution">A solution</h2>
<p>To capture run time relationships, generating diagrams from running code is the only option. And we already have plenty of code that is executed regularly, in the form of tests.</p>
<p>Repositories should already have a good suite of tests (unit, integration and end to end, for example), and each test should be relatively short and simple. </p>
<p>These tests should already embody logical groupings of code, and sensible levels of abstraction. So they are an excellent candidate for generating documentation.</p>
<p>The solution involves instrumenting the code imported by a test. This instrumented code then keeps a record of the run time call hierarchy, and is able to write the results as a <a target="_blank" href="https://mermaid-js.github.io/mermaid/#/">Mermaid markdown diagram</a> (tecnhically a <a target="_blank" href="http://agilemodeling.com/artifacts/sequenceDiagram.htm">sequence diagram</a>).</p>
<p>The code below (<a target="_blank" href="https://github.com/resgroup/docs-from-tests/blob/master/tests/test_hello_world.py">a test from the python package</a>) shows how it works. </p>
<p>For each existing test you create a "wrapper" test, which is responsible for initialising the call hierarchy and saving the diagram. If you have a lot of tests you might want to introduce a <a target="_blank" href="https://realpython.com/primer-on-python-decorators/">decorator</a> to save repetition.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> docs_from_tests.instrument_call_hierarchy <span class="hljs-keyword">import</span> instrument_and_import_package, instrument_and_import_module, initialise_call_hierarchy, finalise_call_hierarchy
<span class="hljs-keyword">from</span> samples.hello_world_combiner <span class="hljs-keyword">import</span> HelloWorldCombiner

<span class="hljs-comment"># you can instrument entire packages / folders at once like this</span>
instrument_and_import_package(os.path.join(Path(__file__).parent.absolute(), <span class="hljs-string">'..'</span>, <span class="hljs-string">'samples'</span>), <span class="hljs-string">'samples'</span>)
<span class="hljs-comment"># You can instrument individual modules like this</span>
<span class="hljs-comment"># instrument_and_import_module('tests.blah')</span>

<span class="hljs-comment"># this is a wrapper around the test that also outputs the documentation / sequence diagram</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_hello_world</span>():</span>
    <span class="hljs-comment"># the initialises the recording of the call hierarchy</span>
    initialise_call_hierarchy(<span class="hljs-string">'start'</span>)

    <span class="hljs-comment"># This runs the actual test</span>
    _test_hello_world()

    <span class="hljs-comment"># this finalises the call hierarchy and returns the root</span>
    root_call = finalise_call_hierarchy()

    <span class="hljs-comment"># this returns a sequence diagram of the call hierarchy</span>
    sequence_diagram = root_call.sequence_diagram(
        show_private_functions=<span class="hljs-literal">False</span>,
        excluded_functions=[
            <span class="hljs-string">'HelloWorldCombiner.__init__'</span>,
        ]
    )

    <span class="hljs-comment"># this writes out the markdown to disk    </span>
    sequence_diagram_filename = os.path.join(os.path.dirname(__file__), <span class="hljs-string">'..'</span>, <span class="hljs-string">'doc'</span>, <span class="hljs-string">'top-level-sequence-diagram.md'</span>)
    Path(sequence_diagram_filename).write_text(sequence_diagram)

<span class="hljs-comment"># this is the original / source test</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_test_hello_world</span>():</span>
    <span class="hljs-keyword">assert</span> HelloWorldCombiner().combine() == <span class="hljs-string">'Hello world'</span>
</code></pre>
<p>Running <code>pytest</code> on this code will result in the test being run, and the <a target="_blank" href="https://github.com/resgroup/docs-from-tests/blob/master/doc/top-level-sequence-diagram.md">markdown "diagram as code"</a> (below) being created in the doc directory:</p>
<pre><code>sequenceDiagram
  start-&gt;&gt;HelloWorldCombiner.combine: calls x1
  HelloWorldCombiner.combine-&gt;&gt;hello: calls x1
  hello--&gt;&gt;HelloWorldCombiner.combine: returns str
  HelloWorldCombiner.combine-&gt;&gt;world: calls x1
  world--&gt;&gt;HelloWorldCombiner.combine: returns str
  HelloWorldCombiner.combine--&gt;&gt;start: returns str
</code></pre><p>This renders as the following diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/docs-from_tests_example_graph-2.png" alt="Example docs-from-tests diagram" width="600" height="400" loading="lazy"></p>
<p>Changes to the diagram will show up in Git and be committed along with the code that caused the change. This means that the change to the code and the change to the diagram are linked and can be seen together.</p>
<p>Private methods would usually be excluded (although it is optional), and you can exclude other functions so that the graph looks as desired. </p>
<p>Because the call hierarchy is stored in a tree structure, excluding a function also excludes all the functions beneath it.</p>
<h2 id="heading-code-quality">Code quality</h2>
<p>Hopefully you already have tests at appropriate levels of abstraction (classically you would have unit, integration and end to end). This makes it easy to create diagrams at these levels. </p>
<p>If not, then the desire to create good diagrams should guide you towards also creating good tests.</p>
<p>Sometimes the diagrams will look a bit crazy, and you may end up ignoring a lot of functions. This is a clue that the code could probably be made simpler. And in this case the desire to create good diagrams should guide you towards simplifying the code.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Hopefully this will inspire you to create and maintain the documentation that your teammates and your future self will thank you for! It's all quite easy to do.  </p>
<p>All the functionality is in a Python package (<a target="_blank" href="https://pypi.org/project/docs-from-tests/">docs-from-tests)</a>, and there is an <a target="_blank" href="https://github.com/ceddlyburge/docs-from-tests-example">example repo that demonstrates how to use it</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How (and why) to embed domain concepts in code ]]>
                </title>
                <description>
                    <![CDATA[ Code should clearly reflect the problem it’s solving, and thus openly expose that problem’s domain. Embedding domain concepts in code requires thought and skill, and doesn't drop out automatically from TDD. However, it is a necessary step on the road... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/embedding-domain-concepts-in-code/</link>
                <guid isPermaLink="false">66bb925a867a396452a80286</guid>
                
                    <category>
                        <![CDATA[ Quality Software ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Code Quality ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #Domain-Driven-Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Tue, 12 Nov 2019 07:48:19 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/11/2015-Gran-Paradiso-007.JPG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Code should clearly reflect the problem it’s solving, and thus openly expose that problem’s domain. Embedding domain concepts in code requires thought and skill, and doesn't drop out automatically from TDD. However, it is a necessary step on the road to writing easily understandable code.</p>
<p>I was at a software craftsmanship meetup recently, where we formed pairs to solve a simplified Berlin Clock Kata. A Berlin Clock displays the time using rows of flashing lights, which you can see below (although in the kata we just output a text representation, and the lights in a row are all the same colour).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/11/berlin-clock-2.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-initial-test-driven-solution">Initial Test Driven solution</h2>
<p>Most pairs used inside out TDD, and there were a lot of solutions that looked something like this (complete <a target="_blank" href="https://github.com/ceddlyburge/berlin-clock-initial-tdd-solution/blob/master/BerlinClock.py">code available on GitHub</a>).</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">berlin_clock_time</span>(<span class="hljs-params">julian_time</span>):</span>
    hours, minutes, seconds = list(map(int, julian_time.split(<span class="hljs-string">":"</span>)))

    <span class="hljs-keyword">return</span> [
        seconds_row_lights(seconds % <span class="hljs-number">2</span>)
        , five_hours_row_lights(hours)
        , single_hours_row_lights(hours % <span class="hljs-number">5</span>)
        , five_minutes_row_lights(minutes)
        , single_minutes_row_lights(minutes % <span class="hljs-number">5</span>)
    ]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">five_hours_row_lights</span>(<span class="hljs-params">hours</span>):</span>
    lights_on = hours // <span class="hljs-number">5</span>
    lights_in_row = <span class="hljs-number">4</span>
    <span class="hljs-keyword">return</span> lights_for_row(<span class="hljs-string">"R"</span>, lights_on, lights_in_row)

<span class="hljs-comment"># ...</span>
</code></pre>
<p>This type of solution drops out naturally from applying inside out TDD to the problem. You write some tests for the seconds row, then some tests for the five hours row, and so on, and then you put it all together and do some refactoring. This solution does expose some of the domain concepts at a glance:</p>
<ul>
<li>There are 5 rows</li>
<li>There is one second row, 2 hour rows and 2 minute rows</li>
</ul>
<p>Some more concepts are available after a bit of digging, but aren't immediately obvious. The rows are made up of lights that can be on (or presumably off), and that the number of lights on is an indication of the time.</p>
<p>However there are some big parts of the problem that are not exposed. And since I haven't yet explained it, you probably don't know exactly how the Berlin Clock works yet.</p>
<h2 id="heading-elevate-the-concepts">Elevate the concepts</h2>
<p>To improve this we can bring some of the details that are buried in the helper functions (such as <code>get_five_hours</code>) closer to the top of the file. This brings you to something like the following (complete <a target="_blank" href="https://github.com/ceddlyburge/berlin-clock-elevated-concepts/blob/master/BerlinClock.py">code available on GitHub</a>), although the downside is that it breaks nearly all of the tests. Solutions like this are rarer on GitHub, but do exist.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">berlin_clock_time</span>(<span class="hljs-params">julian_time</span>):</span>
    hours, minutes, seconds = list(map(int, julian_time.split(<span class="hljs-string">":"</span>)))

    single_seconds = seconds_row_lights(seconds % <span class="hljs-number">2</span>)
    five_hours = row_lights(
        light_colour=<span class="hljs-string">"R"</span>,
        lights_on=hours // <span class="hljs-number">5</span>,
        lights_in_row=<span class="hljs-number">4</span>)
    single_hours = row_lights(
        light_colour=<span class="hljs-string">"R"</span>,
        lights_on=hours % <span class="hljs-number">5</span>,
        lights_in_row=<span class="hljs-number">4</span>)
    five_minutes = row_lights(
        light_colour=<span class="hljs-string">"Y"</span>,
        lights_on=minutes // <span class="hljs-number">5</span>,
        lights_in_row=<span class="hljs-number">11</span>)
    single_minutes = row_lights(
        light_colour=<span class="hljs-string">"Y"</span>,
        lights_on=minutes % <span class="hljs-number">5</span>,
        lights_in_row=<span class="hljs-number">4</span>)

    <span class="hljs-keyword">return</span> [
        single_seconds,
        five_hours,
        single_hours,
        five_minutes,
        single_minutes
    ]

<span class="hljs-comment"># ...</span>
</code></pre>
<p>This improves the concepts that are now exposed at a glance:</p>
<ul>
<li>There are 5 rows</li>
<li>The seconds row is a special case</li>
<li>There are 2 hour rows and 2 minute rows</li>
<li>The rows use different colour lights</li>
<li>The rows have a different number of lights</li>
</ul>
<p>This is pretty good, and is already better that most of the solutions out there. However, it's still a bit mysterious how the rows are related to each other (there are 2 rows to display the hours and the minutes, so presumably these are linked). It's also not obvious what amount of time each light represents.</p>
<h2 id="heading-name-implicit-concepts">Name implicit concepts</h2>
<p>At the moment some of the concepts (such as the amount of time each light represents) are implicit in the code. Making these explicit, and naming them, forces us to understand them and to embed that understanding in the code.</p>
<p>In order to make the amount of time each light represents explicit, it seems like it would be sensible to pass a <code>time_per_light</code> value to <code>row_lights</code>. This means we have to push the calculation of <code>lights_on</code> down into <code>row_lights</code>.</p>
<p>This in turn makes it obvious that there are two kinds of rows: one related to the quotient (<code>\\</code>) of the time value, and one related to the remainder / modulus (<code>%</code>). If we look at the quotient case, we see that the 2nd parameter to the operation is the <code>time_per_light</code>, which is 5 in both cases (5 hours in one case and 5 minutes in the other).</p>
<p>This allows us to write these rows like this:</p>
<pre><code class="lang-python">five_hour_row = row_lights(
    time_per_light=<span class="hljs-number">5</span>,
    value=hours, 
    light_colour=<span class="hljs-string">"R"</span>,
    lights_in_row=<span class="hljs-number">4</span>)
</code></pre>
<p>If we now turn our attention to the remainder case, we realise that <code>time_per_light</code> is always singular (one hour or one minute), as it is filling in the gaps in the quotient case. </p>
<p>For example, the five hours row can represent 0, 5, 10, 15, or 20 hours, but nothing in between. In order to represent any hour, there must be another row to represent +1, +2, +3 and +4. This means that this row must have exactly 4 lights, and that each light must represent 1 hour.</p>
<p>This implies that the remainder case is dependent on the quotient one, which most people would describe as a parent / child relationship.</p>
<p>With this knowledge in hand, we can now create a function for the child remainder rows, and the solution now looks like this (complete <a target="_blank" href="https://github.com/ceddlyburge/berlin-clock">code on GitHub</a>):</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">berlin_clock_time</span>(<span class="hljs-params">julian_time</span>):</span>
    hours, minutes, seconds = list(map(int, julian_time.split(<span class="hljs-string">":"</span>)))

    <span class="hljs-keyword">return</span> [
        seconds_row_lights(
            seconds % <span class="hljs-number">2</span>),
        parent_row_lights(
            time_per_light=<span class="hljs-number">5</span>,
            value=hours, 
            light_colour=<span class="hljs-string">"R"</span>,
            lights_in_row=<span class="hljs-number">4</span>),
        child_remainder_row_lights(
            parent_time_per_light=<span class="hljs-number">5</span>,
            value=hours,
            light_colour=<span class="hljs-string">"R"</span>),
        parent_row_lights(
            time_per_light=<span class="hljs-number">5</span>,
            value=minutes, 
            light_colour=<span class="hljs-string">"Y"</span>,
            lights_in_row=<span class="hljs-number">11</span>),
        child_remainder_row_lights(
            parent_time_per_light=<span class="hljs-number">5</span>,
            light_colour=<span class="hljs-string">"Y"</span>,
            value=minutes)
    ]

<span class="hljs-comment"># ...</span>
</code></pre>
<p>A quick glance at this code now reveals nearly all the domain concepts</p>
<ul>
<li>The first row represents the seconds and is a special case</li>
<li>On the second row each "R" light represents 5 hours</li>
<li>The third row shows the remainder from the second</li>
<li>On the fourth row each "Y" light represents 5 hours</li>
<li>The fifth row shows the remainder from the fourth</li>
</ul>
<p>This took something thinking about, which will have cost us some time / money. But we increased our understanding of the problem while we did it, and most importantly we embedded that knowledge in to the code. This means that the next person to read the code will not have to do this, which will save some time / money. Since we spend about 10 times longer reading code than we do writing it, this is probably a worthwhile endeavour.</p>
<p>Embedding this understanding has also made it harder for future programmers to make mistakes. For example, the concept of parent / child rows didn't exist in earlier examples, and it would be easy to mismatch them. Now the concept is plain to see, and the values are mostly worked out for you. It is also easier to refactor to support new clock variants, for example where lights in the first hours row represent 6 hours.</p>
<h2 id="heading-how-far-should-you-take-it">How far should you take it?</h2>
<p>There are things we can do to take this further. For example the <code>parent_time_per_light</code> of a child row must match the <code>time_per_light</code> of its parent, and there is nothing enforcing this. There is also a relationship between <code>time_per_light</code> and <code>lights_in_row</code> for the parent rows, and again it is not enforced. </p>
<p>However, at the moment we are only required to support one clock variant, so these probably aren't worth doing. When a change is required for the code, we should refactor so that the change is easy (which might be hard) and then make the easy change.</p>
<h2 id="heading-conclusions">Conclusions</h2>
<p>Embedding domain concepts in code requires thought and skill, and TDD won't necessarily do it for you. It takes longer than a naive solution, but makes the code easier to understand, and will very likely save time in the medium term. Time is money, and finding the right balance of spending time now versus saving time later is also an important skill for a professional programmer to have. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to write easily describable code ]]>
                </title>
                <description>
                    <![CDATA[ When code is not describable using words, most people have to do some mental mapping to turn it in to words. This wastes mental energy, and you run the risk of getting the mapping wrong. Different people will map to different words, which leads to co... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/writing-describable-code/</link>
                <guid isPermaLink="false">66bb9273a5fd14123a8b4a3c</guid>
                
                    <category>
                        <![CDATA[ Code Quality ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Wed, 02 Oct 2019 20:34:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/10/writing-describable-code.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When code is not describable using words, most people have to do some mental mapping to turn it in to words. This wastes mental energy, and you run the risk of getting the mapping wrong. Different people will map to different words, which leads to confusion when discussing the code. </p>
<p>This is usually a fertile breeding ground for bugs born out of miscommunication / misunderstanding, and fixing these bugs often introduces new ones, for the same reasons. In the end it becomes code that no one really understands or wants to touch.</p>
<h2 id="heading-example-of-undescribable-code">Example of undescribable code</h2>
<p>It is easy to think that code is already a written language. If it looks simple, it should be easy to read, speak and listen to. However, this is not always the case.</p>
<p>Below is a common solution to deciding whether a year is a leap year.</p>
<pre><code class="lang-python">(divisibleBy(<span class="hljs-number">4</span>) <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> divisibleBy(<span class="hljs-number">100</span>)) <span class="hljs-keyword">or</span> divisibleBy(<span class="hljs-number">400</span>)
</code></pre>
<p>This is not overly complicated code. It calls a functions 3 times, has 3 operators (and, or, not), and has two levels of nesting.</p>
<p>However, if you take a second to try and describe the algorithm in words I think you will find it to be a struggle.</p>
<p>Maybe “A year is leap year if it is divisible by 4 and not divisible by 100, or divisible by 400”?</p>
<p>The trouble with this is that the code has brackets, but the words do not. So they cannot adequately describe the condition, and whether “or divisible by 400” applies to “divisible by 4” or “not divisible by 400”. You could try some hand waving and gesturing to get around this, or vary the length of pause between the statements, but hopefully it’s obvious that there is a lot of potential for error.</p>
<h2 id="heading-refactoring-to-describable-code">Refactoring to describable code</h2>
<p>Instead we can start by describing the condition with words, and then make the words as clear and concise as possible. We might start with this:</p>
<p>“400 years is a special case. If a year is divisible by 400, then it is a leap  year. 100 years is also a special case. If a year is divisible by 100 then it isn’t a leap year, unless it is also divisble by 400, the 400 year special case takes priority. If there are no special cases, then the year is a leap year if it is divisible by 4.”</p>
<p>This is clear, but isn’t concise, so we would probably want to shrink it a bit:</p>
<p>“If a year is divisible by 400, then it is a leap year. Otherwise if it is divisible by 100 then it is a normal year, otherwise it is a leap year if it is divisible by 4.”</p>
<p>If we turn these words in to code, we probably get something like the following:</p>
<pre><code class="lang-python">    <span class="hljs-keyword">if</span> divisbleBy(<span class="hljs-number">400</span>):
        <span class="hljs-keyword">return</span> LeapYear
    <span class="hljs-keyword">elif</span> divisbleBy(<span class="hljs-number">100</span>)
        <span class="hljs-keyword">return</span> NormalYear
    <span class="hljs-keyword">elif</span> divisbleBy(<span class="hljs-number">4</span>):
        <span class="hljs-keyword">return</span> LeapYear
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> NormalYear
</code></pre>
<h2 id="heading-conclusions">Conclusions</h2>
<p>Hard to understand code is a daily occurrence for virtually all programmers. We can help ourselves and our co-workers by writing code that is easy to describe in words.</p>
<p>And the great thing is that doing so is actually easier than writing code any other way, as there is no mental mapping / wasted mental effort. The only “trick” is to describe the algorithm in words, and then write code to match the words.</p>
<p>In many organisations, the algorithm will already be described in words, as part of acceptance tests or user stories, which will improve productivity even further.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to use Netlify Functions in Elm ]]>
                </title>
                <description>
                    <![CDATA[ This worked example creates a simple Netlify Function and integrates it with an Elm application. Netlify functions provide a very convenient way of working with AWS Lambdas, and they have an impressive array of example use cases, such as sending emai... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-netlify-functions-in-elm/</link>
                <guid isPermaLink="false">66bb926adeef71ff683a6d46</guid>
                
                    <category>
                        <![CDATA[ create-elm-app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ netlify-functions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ELM ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Wed, 28 Aug 2019 07:37:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/netlify-functions-elm.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This worked example creates a simple <a target="_blank" href="https://functions.netlify.com/">Netlify Function</a> and integrates it with an Elm application.</p>
<p>Netlify functions provide a very convenient way of working with AWS Lambdas, and they have an <a target="_blank" href="https://functions.netlify.com/examples">impressive array of example use cases</a>, such as sending emails, processing payments and logging.</p>
<p>This example reads secrets from environment variables (to avoid them being exposed in the browser), but it is mostly generic, and can be adapted for other use cases easily.</p>
<h2 id="heading-step-1-prerequisites">Step 1 - Prerequisites</h2>
<ul>
<li>Create a repository for the code, probably on GitHub</li>
<li><code>npm i -g elm</code></li>
<li><code>npm install -g netlify-cli</code></li>
<li><code>npm i -g create-elm-app</code></li>
</ul>
<h2 id="heading-step-2-create-vanilla-elm-app">Step 2 - Create vanilla Elm app</h2>
<ul>
<li><code>create-elm-app elm-app-with-netlify-function</code></li>
<li><code>cd elm-app-with-netlify-function</code></li>
<li><code>elm-app start</code></li>
</ul>
<p>This should start a development server and load the app in your browser.</p>
<p>You can look at <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/d976b2391f98f07113d1e41a64b0359caddf3452">this commit in the companion repository</a> to check that everything is ok.</p>
<h2 id="heading-step-2-deploy-on-netlify">Step 2 - Deploy on Netlify</h2>
<ul>
<li><code>npm init</code> (and fill in sensible values)</li>
<li><code>npm i create-elm-app --save-dev</code> (this adds create-elm-app to package.json, which is used by netlify)</li>
<li>Push the code to GitHub</li>
</ul>
<p>You can see the results of this at <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/aa52ccfabacae69591a920f0675eedf620ae8b03">this commit in the companion repository</a></p>
<ul>
<li>Log in / Sign up / register with <a target="_blank" href="https://www.netlify.com/">Netlify</a></li>
<li>Create a <a target="_blank" href="https://app.netlify.com/start">new site</a> on Netlify</li>
<li>Choose your repository</li>
<li>Set the "Build command" to <code>elm-app build</code></li>
<li>Set the "Public directory" to <code>build</code></li>
<li>Click on "Deploy Site"</li>
</ul>
<p>Netlify will now deploy the site, installing the dependencies specified in package.json, then running <code>elm-app build</code> and then serving the dist directory.</p>
<p>From now on, Netlify will attempt to deploy the latest code every time you push to GitHub.</p>
<h2 id="heading-step-3-link-netlify-dev">Step 3 - Link Netlify Dev</h2>
<ul>
<li><code>netlify login</code></li>
<li><code>netlify link</code> and choose the “Use current git remote url” option</li>
<li>Add “./netlify” to .gitignore</li>
<li>Add a netlify.toml file (from <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/6514012000ea82fb6625fa3686adafa321723d28">this commit in the companion repository</a>)</li>
<li><code>netlify dev</code></li>
</ul>
<p>This should start a local development server and load the app in your browser, in a similar way to step 1.</p>
<h2 id="heading-step-4-add-a-netlify-function">Step 4 - Add a netlify function</h2>
<p>Run <code>netlify functions:create</code> to create a new netlify function. Choose the “js-token-hider” template, and name it "call-api".</p>
<p>This will create a javascript file for the function, and a package.json for its dependencies in “functions/call-api”.</p>
<p>Replace functions/call-api/call-api.js with this one in <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/79381b9c1a7731b01f0c81b58a772d9576f76732">the companion repository</a></p>
<p>Now if you run <code>netlify dev</code>,  the function will be served as well as the app, albeit on different  ports. You can view the function in the browser to check that it is  working (probably at <a target="_blank" href="http://localhost:34567/call-api">http://localhost:34567/call-api</a> or <a target="_blank" href="http://localhost:34567/.netlify/functions/call-api">http://localhost:34567/.netlify/functions/call-api</a>)</p>
<h2 id="heading-step-5-call-the-netlify-function-from-elm">Step 5 - Call the netlify function from Elm</h2>
<p>Install depdencies</p>
<ul>
<li><code>elm install elm/json</code></li>
<li><code>elm install elm/http</code></li>
<li><code>elm install krisajenkins/remotedata</code></li>
</ul>
<p>Update Main.elm to call the function and display the results (from <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/4dc9e8e4b60d061b5d5ef0fb2ce6ab856741236f">the companion repository</a>).</p>
<p>Instruct create-elm-app to proxy api calls to the function, by adding elmapp.config.js, as shown in <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/90a63178e38f2919770e37fcc94e7ee0bec343ab">the companion repository</a>.</p>
<p>At  this point, thee application runs, and successfully calls the api, but  there are no secrets / environment variables yet, so the UI shows an error.</p>
<h2 id="heading-step-6-add-the-secrets">Step 6 - Add the secrets</h2>
<p>Go  to the “Site Settings” - “Build &amp; Deploy” - “Continuous Deployment”  - “Environment Variables” section on the Netlify website for your application.</p>
<p>Add environment variables for API_TOKEN and API_URL</p>
<p>Now when you run ‘netlify dev’ the app should now load in the browser and call the locally hosted netlify function, which will return the API_TOKEN and API_URL environment variables that you set on Netlify.</p>
<p>The  same should be true on the live deployment on Netlify. You may need to  “Trigger Deploy” manually on Netlify, so that it uses the new environment variables.</p>
<p>You can see the deployment of the companion repository at <a target="_blank" href="https://netlify-functions-with-elm.netlify.com">https://netlify-functions-with-elm.netlify.com</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Netlify  / serverless functions are extremely useful for creating / connecting to the backend services that your front end needs. They are also very east to set up, as this artcile (hopefully!) shows.</p>
<p>Create-elm-app is a great tool for developing Elm applications, and its simple proxy feature works well when developing Netlify functions.</p>
<p>Netlify Dev is great for replicating the production Netlify setup when developing locally (in this case by automatically providing the environment variables).</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Scale Elm Views with Master View Types ]]>
                </title>
                <description>
                    <![CDATA[ A concept to help Elm Views scale as applications grow larger and more complicated. In Elm, there are a lot of great ways to scale the Model, and update, but there is more controversy around scaling the view. A lot of the debate is around Reusable Vi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/scaling-elm-views-with-master-view-types/</link>
                <guid isPermaLink="false">66bb9271deef71ff683a6d48</guid>
                
                    <category>
                        <![CDATA[ ELM ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scaling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Thu, 18 Jul 2019 07:29:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/2014-Haute-Route-Imperiale51.JPG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A concept to help Elm Views scale as applications grow larger and more complicated.</p>
<p>In Elm, there are a lot of great ways to scale the <code>Model</code>, and <code>update</code>, but there is more controversy around scaling the <code>view</code>. A lot of the debate is around <a target="_blank" href="https://gist.github.com/rofrol/fd46e9570728193fddcc234094a0bd99#reusable-views-instead-of-nested-components">Reusable Views versus Components</a>. Components are not recommended, but a lot of people are still advocating for them.  This article presents an idea that hopefully strengthens the argument for Resuable Views.</p>
<p>In almost all cases, the scaling problem comes down to enforcing consistency, which usually means allowing child views to make some adjustments to the master view, while at the same time not allowing child views to make a mess.</p>
<p>I will be using Richard Feldman's excellent <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example">Real World app</a> (specifically written to demonstrate scaling in Elm) as an example, as it is contains a lot of current best practice techniques, it is well known (2000+ stars and 300+ forks) and Richard is a well known Elm expert. </p>
<p>I will be suggesting some improvements to this code, so I want make a clear at this point that I mean no disrespect by this (I would bet large sums of money that he did it in about one tenth of the time it would have taken me!). You could also argue that the problems are small and not worth fixing. Ultimately, this decision is yours, but by the end of the article I hope to persuade you that there are problems, and that they are fixable if you think it is worthwhile.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/ski-touring.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-master-view-functions-with-conditionals">Master view functions with conditionals</h2>
<p>One option is to define a master view function. This function takes care of shared concerns, like the header bar and overall layout. Then it calls child view functions depending on the current view and / or has parameters to control child specific behaviour.</p>
<p>This works, but can quickly lead to:</p>
<ul>
<li>An explosion of parameters, potentially forcing your child views to return a lot of things they don't care about.</li>
<li>A mixing of responsibilities between master and child views.</li>
<li>Extra code and duplication.</li>
</ul>
<p>In the Real World App, a parameter of type <code>Page</code> is passed to the master view so that it can render a navbar link as active. There is a <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page.elm#L113">large case statement</a> that uses this parameter to work out what which link is active, and it would be a lot easier for the child just to specify this.</p>
<p>The <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Main.elm#L85">line below</a> shows the master view passing <code>Page.Home</code>, which has to match up with <code>Home.view home</code>. This is easy to get wrong, there is no help from the compiler or type system, and really it is the responsibility of the child view the specify this.</p>
<p><code>viewPage Page.Home GotHomeMsg (Home.view home)</code></p>
<p>There is some duplication when <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page.elm#L62">creating the NavBarLink Html</a>, and the <code>linkTo</code> function will accept any Html, although only very particular Html is valid.</p>
<h2 id="heading-convention-and-trust">Convention and trust</h2>
<p>Another possibility is for child views to be responsible for keeping shared elements consistent, by convention and trust. </p>
<p>Arguably this also happens in the Real World App. The <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Home.elm#L146">Home</a>, <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Article.elm#L119">Article</a> and <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Profile.elm#L197">Profile</a> views all have the concept of a banner. The banner is different in each view, but presumably is meant to be a consistent and recognisable visual element (essentially, it's the title / header for the view). The views don't share any code for these banners, and as a result of this they are not the same size or colour. You could theoretically try and enforce a convention using tests, but it would be difficult, and probably not worthwhile.</p>
<h2 id="heading-helper-functions">Helper functions</h2>
<p>Another possibility is for child views to be responsible for keeping shared elements consistent, but by using some helper functions. This is definitely a step forward, and is probably the most common solution I see in the wild. The functions can go in the same file and be next to each other. This makes it easier to see that they are related and are representing the same visual element, and easier to make them consistent. </p>
<p>However, there are still some drawbacks. The main one is that the child views have to know to use the helper functions, and there is nothing enforcing this. This isn't a huge deal when you only have one shared element and one function to call, but as applications get bigger, you end up with a combinatorial explosion of differences in the shared visual elements. Most people tame this by providing a number of small, focused functions for the various differences. Then the child view has to know about all these functions, and how to compose them, and there no help from the compiler. </p>
<p>Again, this arguably occurs in the Real World App: for example in <a target="_blank" href="https://github.com/rtfeldman/elm-spa-example/blob/b5064c6ef0fde3395a7299f238acf68f93e71d03/src/Page/Profile.elm#L211">this part of the Profile.view function</a>, which needs to know how to use the <code>viewTabs</code>, <code>Feed.viewArticles</code> and <code>Feed.viewPagination</code> helper functions, and what Html they need to be contained in.</p>
<h2 id="heading-scaling-with-master-view-types">Scaling with Master View Types</h2>
<p>In order to overcome these problems, I propose using a <code>Type</code> to define your site structure (I rather pompously call this a "Master View Type"). Child views then return this type, and the master view takes it as a parameter and returns the html. </p>
<p>For the Real World App examples we have been looking at, the Master View Type is below (<code>Viewer</code> is the person viewing the page in the Real World App). You could arguably have more general banner types here, such as AvatarBanner, or even IconBanner (instead of ViewerBanner) depending on your domain.</p>
<pre><code class="lang-elm"><span class="hljs-keyword">type</span> <span class="hljs-keyword">alias</span> <span class="hljs-type">Page</span> =
    {   activeNavBarLink: <span class="hljs-type">NavBarLink</span>
        , banner: <span class="hljs-type">Banner</span>
        , body: <span class="hljs-type">Html</span> <span class="hljs-type">Msg</span>
    }

<span class="hljs-keyword">type</span> <span class="hljs-type">Banner</span> =
    <span class="hljs-type">TextBanner</span> <span class="hljs-type">TextBannerProperties</span>
    | <span class="hljs-type">ViewerBanner</span> <span class="hljs-type">Viewer</span>
    | <span class="hljs-type">ArticleBanner</span> <span class="hljs-type">Viewer</span> <span class="hljs-type">ArticlePreview</span>

<span class="hljs-keyword">type</span> <span class="hljs-type">NavBarLink</span> =
    <span class="hljs-type">NavBarLink</span> <span class="hljs-type">NavBarLinkProperties</span>
</code></pre>
<p>To demonstrate this, I have create a repository with just the <a target="_blank" href="https://github.com/ceddlyburge/elm-without-master-view-types">Header and Banner parts of the Real World App</a>  and then created a new repository after refactoring to use a  <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/Page.elm">Master Page Type</a>, <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/NavBarLink.elm">NavBarLink Type</a> and <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/Banner.elm">Banner Type</a>. You can peruse the code to get a feel for how it works.</p>
<p>To my mind, using a Master Page Type has the following benefits:</p>
<ul>
<li>Writing the master view code is easier</li>
<li>Writing the child view code is easier</li>
<li>Communication and understanding are improved, as UI concepts now have names</li>
<li>Theming / redesigning a site is a lot easier</li>
<li>Elm packages can provide UI templates</li>
</ul>
<p>The master view can precisely define what it will accept / support via the types, with <a target="_blank" href="https://guide.elm-lang.org/types/custom_types.html">union types</a> and <a target="_blank" href="https://medium.com/@ckoster22/advanced-types-in-elm-opaque-types-ec5ec3b84ed2">opaque types</a>. Non supported combinations can be made unrepresentable or uncreatable. </p>
<p>In my example repository the <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/NavBarLink.elm">NavBarLink type is opaque</a>, so it is only possible to create supported NavBarLinks (<code>home</code>, <code>article</code> and <code>viewer</code>). In a similar way <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types/blob/master/src/Banner.elm">Banner is a union type</a>, which means that only supported variants can be represented. </p>
<p>It would be possible for a programmer to simply change these files, but a proficient programmer would recognise the patterns and follow them. If this isn't enough and you are feeling paranoid, then you can require stricter code review on such files, potentially taking advantage of <a target="_blank" href="https://help.github.com/en/articles/about-code-owners">CODEOWNERS</a> functionality on GitHub and GitLab. In the extreme you can  provide the modules via an elm package, and restrict push access to the underlying repository.</p>
<p>Child views don't have to do anything more than create an instance of the types. The helper functions all return types, so it's easy to see which functions can be used in a particular context, and is impossible to use functions in the wrong context. For example, if a function returns a <code>HeaderBarLink</code>, it is impossible to mistakenly use this function to create a link in the <code>FooterBar</code>, or elsewhere on the page. Child views can also leave some of the complexity to the master view. For example, the child view can define a list of options to choose from, and the master view can render this using buttons, a drop down list or an autocomplete list, depending on the number of options. </p>
<p>The master page type also provides names for UI concepts, which can then be discussed. For example, a designer could say "Let's move the NavBarLinks to the left hand side", and everybody would know what they meant. A product owner could say "Let's create a new page with an IconBanner, and we'll use the current weather api for the icon" and again, everybody would know what they mean. You can look at this <a target="_blank" href="https://www.thoughtworks.com/insights/blog/ui-components-design">excellent thoughtworks article</a> for more details of this.</p>
<p>Since the responsibility for turning the Master View Type in to html is all in the same place, it is easy to make drastic changes to the look and feel of a website, and to do theming. These changes and themes can alter the Css <em>and the Html</em>, which is something that the normal theming techniques just can't do. Pragmatically, your Master View Type will often have a <code>body: Html Msg</code> property (to allow child views complete flexibility on the child specific parts of the page) so there would still be some sprawling code to fix up, but it will definitely be a lot easier.</p>
<p>Finally, it opens up possibility of providing ready made themes and site layouts as packages. This would allow you to just do the following to get a working app, complete with layout and styling:</p>
<ul>
<li><code>create-elm-app</code></li>
<li><code>elm install elm-bootstrap-starter-template</code></li>
<li>Write some code to create the Master Page Type</li>
<li><code>elm-app start</code></li>
</ul>
<p>Companies could create packages like these to ensure a consistent look and feel across their applications. Open source designs and layouts could emerge and become commonplace, similar to the way that Bootstrap has revolutionised html and css design. Developers with limited design skills (like me) could concentrate on the the bits they are best at (the logic), but still produce produce elegant websites using these packages.</p>
<p>To demonstrate this I have created a <a target="_blank" href="https://package.elm-lang.org/packages/ceddlyburge/elm-bootstrap-starter-master-view/latest/">bootstrap starter master view package</a>. It mimics the layout and design of the <a target="_blank" href="https://getbootstrap.com/docs/4.0/examples/starter-template/">bootstrap starter template</a>. I have then used this package in a demo elm application. You can <a target="_blank" href="https://elm-bootstrap-starter.netlify.com/">browse the demo application</a> to see how it looks, and <a target="_blank" href="https://github.com/ceddlyburge/elm-bootstrap-starter-demo">view the source</a> to see how it works.</p>
<p>All these advantages come at a small to negative cost. There is a little more code for the new types, but some duplication is removed. You can view the source of the Real World App repositories <a target="_blank" href="https://github.com/ceddlyburge/elm-without-master-view-types">from before</a> and <a target="_blank" href="https://github.com/ceddlyburge/elm-master-view-types">after refactoring to use a Master Page Type</a> for the full details.</p>
<h2 id="heading-conclusions">Conclusions</h2>
<p>Master View Types bring a lot of benefits (view code is easier to write and maintain, UI concepts are named and UI packages are possible) for little or no cost. They should improve the code of any Elm application that has issues around enforcing consistency (while allowing flexibility) in their view code, which in my experience is most medium and large applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to use GitHub as a PyPi server ]]>
                </title>
                <description>
                    <![CDATA[ I was looking for a hosted private PyPi Python Package server, that used credentials that the team already has (such as GitHub). I didn’t want to create an on-premises server. For us, it would make it impossible to use cloud-based build servers, and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-github-as-a-pypi-server-1c3b0d07db2/</link>
                <guid isPermaLink="false">66bb9267deef71ff683a6d44</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Thu, 15 Nov 2018 17:59:30 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*E3Pn3GrE2DBJRBV8r1W__w.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I was looking for a hosted private PyPi Python Package server, that used credentials that the team already has (such as GitHub).</p>
<p>I didn’t want to create an on-premises server. For us, it would make it impossible to use cloud-based build servers, and it is another moving part that can go wrong. There are also potential issues with fine-grained security and speed. (We have a worldwide team, so serving the content via a <a target="_blank" href="https://www.webopedia.com/TERM/C/CDN.html">CDN</a> would be helpful.)</p>
<p>I didn’t want to force the team to create accounts with another provider. They already have Active Directory and GitHub accounts. It is an annoyance for them and creates a governance burden for me.</p>
<p>Sadly, I couldn’t find such a service. <a target="_blank" href="https://gemfury.com/">GemFury</a> is excellent but doesn't support GitHub authorization (at the team / organisation level) and <a target="_blank" href="https://www.packagr.app">Packagr</a> doesn’t support GitHub authorisation at all. <a target="_blank" href="https://docs.myget.org/">MyGet</a> is also excellent, it does allow me to use GitHub authorization, but doesn’t host Python packages. Azure DevOps has something that looks promising, but it’s in <a target="_blank" href="https://docs.microsoft.com/en-us/azure/devops/artifacts/quickstarts/python-packages?view=vsts">private beta</a> at the moment.</p>
<p>Happily, this is possible using cloud Git repositories such as GitHub, GitLab and BitBucket.</p>
<h3 id="heading-pip-can-install-packages-from-git">Pip can install packages from Git</h3>
<p>I have hosted a Python package on GitHub (<a target="_blank" href="https://github.com/ceddlyburge/python_world">python_world</a>), which you can install with the following command (make sure you trust me before running this command and installing my code on your computer).</p>
<p><code>pip install git+https://github.com/ceddlyburge/python_world#egg=python_world</code></p>
<p>Pip provides options to install from head, from a branch, from a tag or from a commit. I usually tag each release and install from these tags. See the <a target="_blank" href="https://pip.pypa.io/en/stable/reference/pip_install/#git">pip install documentation for full details</a>.</p>
<p>This repository is public, but it works just the same with a private repo, as long as you have permission. There is no special magic (it's a vanilla Python package) and <a target="_blank" href="https://github.com/ceddlyburge/python_world/blob/master/setup.py">Setup.py</a> does most of the work as normal.</p>
<p>If you are new to creating Python Packages, the <a target="_blank" href="https://packaging.python.org/tutorials/packaging-projects/">Packaging Python Projects tutorial</a> is worth a quick read.</p>
<h3 id="heading-setuptools-can-also-install-dependencies-from-git">Setuptools can also install dependencies from Git</h3>
<p><a target="_blank" href="https://pypi.org/project/setuptools/">Setuptools</a> is how most people create Python packages.</p>
<p>I have hosted another package on GitHub <a target="_blank" href="https://github.com/ceddlyburge/python_hello">python_hello</a>, which depends on <a target="_blank" href="https://github.com/ceddlyburge/python_world">python_world</a>. (I’m sure you can see where this is going.)</p>
<p>The relevant bits from setup.py are below. <code>install_requires</code> specifies that <code>python_world</code> is a required dependency and tells Setuptools where to find it.</p>
<pre><code class="lang-python">install_requires=[
    <span class="hljs-string">'python_world@git+https://github.com/ceddlyburge/python_world#egg=python_world-0.0.1'</span>,
]
</code></pre>
<p>You can install this package using the command below. It will also download the dependent <code>python_world</code> package.</p>
<p><code>pip install git+https://github.com/ceddlyburge/python_hello#egg=python_hello</code></p>
<p>This links to a specific version of <code>python_world</code>, which is a shame as it means that pip can't do any dependency management (such as working out an acceptable version if multiple things are reliant on it). However, by the end of this article, we will have removed the need for the specific link.</p>
<h3 id="heading-python-environments">Python environments</h3>
<p>As everyone who has used Python without an environment knows, environments save a lot of frustration and wasted time. So we need to support those.</p>
<p>I have created a repo (<a target="_blank" href="https://github.com/ceddlyburge/python_use_hello_world">use-hello-world</a>) that defines <code>python_hello</code> as a dependency in <a target="_blank" href="https://github.com/ceddlyburge/python_use_hello_world/blob/master/requirements.txt">requirements.txt</a> for <a target="_blank" href="https://virtualenv.pypa.io/">Virtualenv</a>, and <a target="_blank" href="https://github.com/ceddlyburge/python_use_hello_world/blob/master/environment.yml">environment.yml</a> for <a target="_blank" href="https://www.anaconda.com">Conda</a>.</p>
<p>If you download the repo, you can install the dependencies into a virtualenv with the following command.</p>
<p><code>pip install -r requirements.txt</code></p>
<p>If you are using conda you can use this command:</p>
<p><code>conda env create -n use-hello-world</code></p>
<h3 id="heading-pypi-index">PyPi Index</h3>
<p>So far we are able to install packages from our private Git repositories. These packages can, in turn, define dependencies to other private repositories. There still isn’t a PyPi server in sight.</p>
<p>We could stop at this point. However, the syntax for defining dependencies is a bit mysterious. It would be difficult for the team to discover which packages are available, and we are linking to specific versions of dependent packages, instead of letting pip manage it.</p>
<p>To fix this we can set up a PyPi index that conforms to <a target="_blank" href="https://www.python.org/dev/peps/pep-0503">Pep 503</a>. This specification is quite simple, and I have just created the index by hand. If this becomes too cumbersome I can generate it from the GitHub API.</p>
<p>I created this <a target="_blank" href="https://ceddlyburge.github.io/python-package-server/">PyPi Index</a> using GitHub Pages. There are equivalent things for GitLab and BitBucket. You can see that the <a target="_blank" href="https://github.com/ceddlyburge/python-package-server/">source code</a> is very simple. GitHub Pages sites are always public (and there is probably no sensitive information in your index). However, if you need them to be private you can use a service such as <a target="_blank" href="https://www.privatehub.cloud/">PrivateHub</a>.</p>
<p>One thing to look out for is the <a target="_blank" href="https://www.python.org/dev/peps/pep-0503/#normalized-names">name normalisation</a> of the specification. This requires the <code>python_hello</code> package information to be present at <code>python-hello/index.html</code> (note the change from an underscore to a dash).</p>
<p>Now that we have a PyPi server, we can install packages using the command below.</p>
<p><code>pip install python_hello --extra-index-url [https://ceddlyburge.github.io/python-package-server/](https://ceddlyburge.github.io/python-package-server/)</code></p>
<p>So that you can see this working with environments, I have created another repo (<a target="_blank" href="https://github.com/ceddlyburge/python_use_hello_world_from_server">use_hello_world_from_server</a>) that defines the <code>python_hello</code> dependency using this PyPi index instead of direct GitHub Links. If you are trying it with Conda, version &gt;4.4 is required.</p>
<p>At this point, we can go back and remove the direct Git link in <a target="_blank" href="https://github.com/ceddlyburge/python_hello_world/blob/master/setup.py">install_requires in setup.py of python_hello</a> (as Setuptools will be able to find it from our server).</p>
<h3 id="heading-conclusions">Conclusions</h3>
<p>Using a cloud-hosted Git provider as a PyPi server is a viable option. If you are already using one, that means that you can reuse the credentials and permissions that you already have. It will work with Cloud build servers and is likely to be provided via a CDN, so will be fast worldwide. It requires more knowledge to set up than a hosted server, but probably the same or less than hosting your own server on premises.</p>
<h3 id="heading-hints-and-tips">Hints and tips</h3>
<p>Serving the index locally can help to troubleshoot problems (such as name normalization). It’s easy to see what requests are being made. You can use the inbuilt python HTTP server for this (<code>python -m Http.Server -8000</code>). This led me to find out that <code>pip search</code> uses <code>post</code>requests, so won’t work with GitHub pages.</p>
<p>You can run <code>python setup.py -install</code> to check your pip packages locally, before pushing them to Git.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
