<?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[ tools - 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[ tools - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 25 Jul 2026 22:28:05 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/tools/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 7 Tools Digital Nomads Need in 2026 ]]>
                </title>
                <description>
                    <![CDATA[ Digital nomadism has changed dramatically over the last few years. What started as a lifestyle trend for freelancers and travel creators has evolved into a serious way of working for developers, consu ]]>
                </description>
                <link>https://www.freecodecamp.org/news/tools-digital-nomads-need-in-2026/</link>
                <guid isPermaLink="false">6a0773cec013adcbdafe0aea</guid>
                
                    <category>
                        <![CDATA[ Digital Nomads ]]>
                    </category>
                
                    <category>
                        <![CDATA[ remote ]]>
                    </category>
                
                    <category>
                        <![CDATA[ remote work ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Fri, 15 May 2026 19:28:14 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/2c176eb4-df1e-4b40-8551-8b4b691a6ffe.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Digital nomadism has changed dramatically over the last few years. What started as a lifestyle trend for freelancers and travel creators has evolved into a serious way of working for developers, consultants, designers, marketers, startup founders, and even enterprise employees.</p>
<p>In 2026, remote work is no longer tied to a single city or office. Professionals are building careers while moving between countries, time zones, and temporary workspaces.</p>
<p>But behind the freedom of working from anywhere is a hidden reality that many people do not talk about enough. Successful digital nomads depend heavily on systems and infrastructure. Without the right tools, even simple tasks become difficult.</p>
<p>Video calls fail, files get lost, payments become delayed, and productivity disappears quickly. The modern digital nomad needs tools that create stability while constantly moving.</p>
<p>Here are seven essential tools digital nomads rely on in 2026.</p>
<ul>
<li><p><a href="#heading-cloud-workspaces-for-organized-remote-work">Cloud Workspaces for Organized Remote Work</a></p>
</li>
<li><p><a href="#heading-financial-platforms-for-international-payments">Financial Platforms for International Payments</a></p>
</li>
<li><p><a href="#heading-international-esim-services-for-reliable-connectivity">International Services for Reliable Connectivity</a></p>
</li>
<li><p><a href="#heading-password-managers-for-security">Password Managers for Security</a></p>
</li>
<li><p><a href="#heading-vpn-services-for-privacy-and-safe-browsing">VPN Services for Privacy and Safe Browsing</a></p>
</li>
<li><p><a href="#heading-communication-platforms-for-distributed-teams">Communication Platforms for Distributed Teams</a></p>
</li>
<li><p><a href="#heading-ai-productivity-tools-for-faster-workflows">AI Productivity Tools for Faster Workflows</a></p>
</li>
<li><p><a href="#heading-digital-nomadism-is-becoming-more-infrastructure-driven">Digital Nomadism Is Becoming More Infrastructure-Driven</a></p>
</li>
</ul>
<h3 id="heading-cloud-workspaces-for-organized-remote-work"><strong>Cloud Workspaces for Organized Remote Work</strong></h3>
<p>The first thing every digital nomad needs is a reliable cloud workspace. Working remotely across multiple countries becomes chaotic very quickly without centralized systems for files, notes, tasks, and communication.</p>
<p>Laptops can fail, bags can get lost, and internet connections can become unstable. Storing everything locally creates unnecessary risk.</p>
<p>This is why most remote workers now rely on platforms like Google Workspace, Notion, Microsoft 365, and Dropbox.</p>
<p><a href="https://workspace.google.com/">Google Workspace</a> remains one of the most widely used productivity ecosystems because it combines email, documents, spreadsheets, cloud storage, and calendar management into a single platform. For distributed teams, the ability to collaborate in real time is extremely valuable.</p>
<p>Notion has also become popular among digital nomads because it combines note-taking, project management, documentation, and knowledge management into one flexible workspace.</p>
<p>These tools create consistency. Whether someone is working from a co-working space in Bali, a cafe in Lisbon, or an apartment in Bangkok, their work environment stays largely the same.</p>
<p><a href="https://www.cognizant.com/us/en/glossary/cloud-workspace">Cloud workspaces</a> also reduce dependency on hardware. If a laptop breaks during travel, work can continue from another device with minimal disruption.</p>
<p>For long-term remote work, that reliability matters more than most people expect.</p>
<h3 id="heading-financial-platforms-for-international-payments"><strong>Financial Platforms for International Payments</strong></h3>
<p><a href="https://www.thomascook.in/blog/forex/travelling-abroad-10-tips-for-managing-your-finances/">Managing money internationally</a> used to be one of the biggest challenges for remote workers.</p>
<p>Traditional banks were not designed for people moving constantly between countries. Currency conversion fees, transfer delays, and international payment restrictions created major friction for freelancers and remote employees.</p>
<p>In 2026, digital nomads increasingly depend on financial platforms built specifically for global work.</p>
<p>Wise has become one of the most trusted solutions for international transfers because it offers transparent exchange rates and lower conversion fees compared to many banks.</p>
<p>Revolut is also widely used because it combines multi-currency accounts, virtual cards, travel spending controls, and mobile banking features into one platform.</p>
<p>Payoneer remains popular among freelancers working with international clients and marketplaces.</p>
<p>These tools help digital nomads receive payments faster, manage multiple currencies, and reduce losses from exchange rates.</p>
<p>Expense tracking is equally important. Constant travel can create uncontrolled spending very quickly. Flights, accommodations, <a href="https://www.skuad.io/blog/15-amazing-co-working-spaces-around-the-world-for-remote-workers">co-working memberships</a>, transport, and insurance costs add up fast.</p>
<p>The most successful digital nomads usually approach finances with discipline. Sustainable remote work depends heavily on maintaining predictable cash flow and financial visibility.</p>
<p>Global mobility becomes much easier when financial infrastructure works smoothly.</p>
<h3 id="heading-international-services-for-reliable-connectivity"><strong>International Services for Reliable Connectivity</strong></h3>
<p>Internet access is the foundation of digital nomadism. Without reliable connectivity, remote work becomes impossible. Meetings disconnect, cloud applications fail, uploads stop midway, and communication slows down immediately.</p>
<p>For years, travelers depended on local SIM cards in every country they visited. That process was frustrating and inefficient. Finding telecom stores after landing, dealing with language barriers, and switching physical SIM cards repeatedly created unnecessary stress.</p>
<p>In 2026, most experienced digital nomads prefer using an <a href="https://saily.com/">international eSIM</a> instead.</p>
<p>An International eSIM makes cross-border travel much smoother. Remote workers can often activate connectivity before even landing in a new country.</p>
<p>This creates a major advantage during travel days. Internet access works immediately for maps, messaging, ride-sharing apps, banking verification, and work communication.</p>
<p>Consistency is another important benefit. Many remote workers now depend on International eSIM services because they reduce downtime between locations.</p>
<p>This matters professionally. Missing meetings or becoming unreachable during client communication can damage credibility quickly.</p>
<p>For digital nomads moving regularly between countries, connectivity is no longer just a convenience. It is operational infrastructure.</p>
<h3 id="heading-password-managers-for-security"><strong>Password Managers for Security</strong></h3>
<p>Cybersecurity risks increase significantly when working remotely.</p>
<p>Digital nomads regularly connect to airport Wi-Fi, hotel networks, cafes, co-working spaces, and temporary apartment internet connections. Many of these networks are not secure.</p>
<p>Using <a href="https://www.lastpass.com/features/password-generator">weak passwords</a> or storing credentials carelessly creates major risks for both personal and professional systems.</p>
<p>This is why password managers have become essential tools for remote workers.</p>
<p>Platforms like 1Password, Bitwarden, and LastPass help users generate strong passwords, store credentials securely, and synchronize login access across devices.</p>
<p>Instead of remembering dozens of passwords manually, users can protect accounts through encrypted password vaults and multi-factor authentication.</p>
<p>For remote professionals handling company systems, client dashboards, or financial accounts, this layer of security is extremely important.</p>
<p>Password managers also reduce the risk of phishing attacks and credential reuse, which remain some of the most common cybersecurity problems globally.</p>
<p>In distributed work environments, operational security is becoming part of everyday professional responsibility.</p>
<h3 id="heading-vpn-services-for-privacy-and-safe-browsing"><strong>VPN Services for Privacy and Safe Browsing</strong></h3>
<p>VPN services have become standard tools for remote professionals. A VPN, or <a href="https://www.freecodecamp.org/news/vpns-vs-proxies-what-are-the-differences/">virtual private network</a>, encrypts internet traffic and helps protect users when accessing the internet through public or untrusted networks.</p>
<p>Services like NordVPN, ExpressVPN, and Proton VPN are commonly used by digital nomads because they improve both privacy and security.</p>
<p>Many remote workers access internal company systems, financial platforms, or customer information while traveling. Doing this over public internet connections without protection introduces significant risk.</p>
<p>VPNs also help maintain more stable access to online services across different regions.</p>
<p>Some platforms and websites behave differently depending on the country someone is connecting from. VPNs help reduce these inconsistencies and improve accessibility while traveling.</p>
<p>For freelancers and consultants, using a VPN also demonstrates professionalism and awareness of cybersecurity best practices.</p>
<p>As remote work becomes more global, companies are increasingly expecting workers to follow stronger operational security standards.</p>
<h3 id="heading-communication-platforms-for-distributed-teams"><strong>Communication Platforms for Distributed Teams</strong></h3>
<p>Communication quality often determines whether remote work succeeds or fails.</p>
<p>Digital nomads work across countries and time zones, which means communication systems need to be reliable, flexible, and easy to access from anywhere.</p>
<p>Platforms like Slack, Zoom, Google Meet, and Microsoft Teams have become core parts of modern distributed work.</p>
<p>Slack is widely used for team collaboration because it allows fast communication without relying entirely on email. Zoom and Google Meet remain essential for client calls, interviews, workshops, and team meetings.</p>
<p>But modern remote work is becoming increasingly asynchronous.</p>
<p>Many experienced digital nomads now avoid unnecessary meetings and instead rely more on shared documentation, recorded updates, and organized messaging systems.</p>
<p>This helps reduce burnout and creates more flexibility around travel schedules and time zones.</p>
<p>Strong communication infrastructure also creates professional consistency. Clients and employers care less about where someone works from and more about responsiveness, reliability, and clarity.</p>
<p>The right communication tools help maintain that trust.</p>
<h3 id="heading-ai-productivity-tools-for-faster-workflows"><strong>AI Productivity Tools for Faster Workflows</strong></h3>
<p>Artificial intelligence has become deeply integrated into remote work in 2026. AI tools are now helping digital nomads automate repetitive work, summarize information, organize tasks, generate drafts, and accelerate research.</p>
<p>Platforms like ChatGPT, Claude, Grammarly, and Perplexity are increasingly part of everyday workflows for remote professionals.</p>
<p>Writers use AI for outlining and editing. Developers use it for debugging and documentation. Marketers use it for content strategy and analysis. Startup founders use it for operational planning and research.</p>
<p>For digital nomads, AI tools are especially valuable because they reduce mental overload.</p>
<p>Travel itself requires constant decision-making. Flights, accommodations, schedules, visas, transportation, and timezone changes all consume attention and energy. AI tools help reduce the amount of manual work needed during busy travel periods.</p>
<p>The professionals gaining the most value from AI are usually those who already understand their field well.</p>
<p>AI works best as an accelerator for skilled workers rather than a replacement for expertise.</p>
<p>As remote work continues evolving, AI literacy is becoming an increasingly important professional advantage.</p>
<h3 id="heading-digital-nomadism-is-becoming-more-infrastructure-driven"><strong>Digital Nomadism Is Becoming More Infrastructure-Driven</strong></h3>
<p>The image of digital nomadism has changed significantly. In the past, the lifestyle was often presented as spontaneous and carefree. In reality, sustainable remote work depends heavily on systems, preparation, and operational reliability.</p>
<p>The most successful digital nomads are usually the people who build strong infrastructure around their work.</p>
<p>Cloud workspaces keep projects organized. Financial platforms simplify international payments. International eSIM services maintain reliable connectivity across borders. VPNs and password managers improve security. Communication systems support distributed collaboration. AI tools reduce workload and improve efficiency.</p>
<p>Together, these tools create stability in an otherwise highly mobile lifestyle.</p>
<p>As companies continue embracing remote and distributed work models, digital nomadism will likely become even more mainstream over the next decade.</p>
<p>The professionals who succeed long term will not necessarily be the ones traveling the most.</p>
<p>They will be the ones who know how to build systems that allow them to work consistently from anywhere in the world.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build AI Agents That Remember User Preferences (Without Breaking Context) ]]>
                </title>
                <description>
                    <![CDATA[ Why Personalization Breaks Most AI Agents Personalization is one of the most requested features in AI-powered applications. Users expect an agent to remember their preferences, adapt to their style, and improve over time. In practice, personalization... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-ai-agents-that-remember-user-preferences-without-breaking-context/</link>
                <guid isPermaLink="false">698cc32db8fec0245bd9996d</guid>
                
                    <category>
                        <![CDATA[ ai agents ]]>
                    </category>
                
                    <category>
                        <![CDATA[ llm ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ System Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ observability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nataraj Sundar ]]>
                </dc:creator>
                <pubDate>Wed, 11 Feb 2026 17:58:05 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770832641633/da49bdca-617e-4272-b5b7-012f3c6c1d61.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-why-personalization-breaks-most-ai-agents"><strong>Why Personalization Breaks Most AI Agents</strong></h2>
<p>Personalization is one of the most requested features in AI-powered applications. Users expect an agent to remember their preferences, adapt to their style, and improve over time.</p>
<p>In practice, personalization is unfortunately also one of the fastest ways to break an otherwise working AI agent.</p>
<p>Many agents start with a simple idea: keep adding more conversation history to the prompt. This approach works for demos, but it quickly fails in real applications. Context windows grow too large. Irrelevant information leaks into decisions. Costs increase. Debugging becomes nearly impossible.</p>
<p>If you want a personalized agent that survives production, you need more than a large language model. You need a way to connect the agent to tools, manage multi-step workflows, and store user preferences safely over time – without turning your system into a tangled mess of prompts and callbacks.</p>
<p>In this tutorial, you’ll learn how to design a personalized AI agent using three core building blocks:</p>
<ul>
<li><p><strong>Agent Development Kit (ADK)</strong> to orchestrate agent reasoning and execution</p>
</li>
<li><p><strong>Model Context Protocol (MCP)</strong> to connect tools with clear boundaries</p>
</li>
<li><p><strong>Long-term memory</strong> to store preferences without polluting context</p>
</li>
</ul>
<p>Rather than focusing on setup commands or vendor-specific walkthroughs, we'll focus on the architectural patterns that make personalized agents reliable, debuggable, and maintainable.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770578645884/2fd77443-31d5-4db3-98f0-bba685122a6f.png" alt="User preferences influence an AI agent’s personalized response" class="image--center mx-auto" width="1452" height="578" loading="lazy"></p>
<p><em>Figure 1 — Personalization influences agent responses</em></p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#what-personalized-means-in-a-real-ai-agent">What “Personalized” Means in a Real AI Agent</a></p>
</li>
<li><p><a class="post-section-overview" href="#how-the-agent-architecture-fits-together">How the Agent Architecture Fits Together</a></p>
</li>
<li><p><a class="post-section-overview" href="#how-to-design-the-agent-core-with-adk">How to Design the Agent Core with ADK</a></p>
</li>
<li><p><a class="post-section-overview" href="#how-to-connect-tools-safely-with-mcp">How to Connect Tools Safely with MCP</a></p>
</li>
<li><p><a class="post-section-overview" href="#how-to-add-long-term-memory-without-polluting-context">How to Add Long-Term Memory Without Polluting Context</a></p>
<ul>
<li><a class="post-section-overview" href="#privacy-consent-and-lifecycle-controls-production-checklist">Privacy, Consent, and Lifecycle Controls (Production Checklist)</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#how-the-end-to-end-agent-flow-works">How the End-to-End Agent Flow Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#common-pitfalls-youll-hit-and-how-to-avoid-them">Common Pitfalls You’ll Hit (and How to Avoid Them)</a></p>
</li>
<li><p><a class="post-section-overview" href="#what-you-learned-and-where-to-go-next">What You Learned and Where to Go Next</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To follow along with this tutorial, you should have:</p>
<ul>
<li><p>Basic familiarity with Python</p>
</li>
<li><p>A general understanding of how large language models work</p>
</li>
<li><p>Optional: a Google Cloud account if you want to run an end-to-end demo. Otherwise, you can follow the architecture and code patterns locally with stubs. We’ll avoid deep infrastructure setup and focus on design patterns rather than deployment mechanics.</p>
</li>
</ul>
<p>You don’t need prior experience with ADK or MCP. I’ll introduce each concept as it appears.</p>
<h2 id="heading-what-personalized-means-in-a-real-ai-agent"><strong>What “Personalized” Means in a Real AI Agent</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770578714303/4d25a7e4-fcdd-4a1a-a12c-411e41f2021f.png" alt="An AI agent accesses external tools through a protocol boundary/control layer" class="image--center mx-auto" width="1382" height="670" loading="lazy"></p>
<p><em>Figure 2 — Keep preferences out of the prompt: agent ↔ tools across a protocol boundary</em></p>
<p>Before writing any code, it’s important to define what personalization means in an AI agent.</p>
<p>Personalization is not the same as “remembering everything.” In practice, agent state usually falls into three categories:</p>
<ol>
<li><p><strong>Short-term context:</strong> Information needed to complete the current task. This belongs in the prompt.</p>
</li>
<li><p><strong>Session state:</strong> Temporary decisions or selections made during a workflow. This should be structured and scoped to a session.</p>
</li>
<li><p><strong>Long-term memory:</strong> Durable user preferences or facts that should persist across sessions.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770577191953/3df5aa02-2eb9-4214-bbef-52f18ddb353a.png" alt="Three panels comparing short-term context, session state, and long-term memory" class="image--center mx-auto" width="946" height="510" loading="lazy"></p>
<p><em>Figure 3 — Three kinds of agent state: context (now), session (today), memory (always)</em></p>
<p>Most problems happen when these categories are mixed together.</p>
<p>If you store long-term preferences directly in the prompt, the agent’s behavior becomes unpredictable. If you store everything permanently, memory grows without bounds. If you don’t scope memory at all, unrelated sessions start influencing each other.</p>
<p>A well-designed, personalized agent treats memory as a first-class system component, not as extra text added to a prompt.</p>
<p>In the next section, we'll look at how to structure the agent so these concerns stay separated. </p>
<p>By the end of this tutorial, you’ll understand how to design a personalized AI agent that uses long-term memory safely, connects to tools through clear boundaries, and remains debuggable as it grows.</p>
<h2 id="heading-how-the-agent-architecture-fits-together"><strong>How the Agent Architecture Fits Together</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770577351960/9b14cadf-d650-4098-8ce1-9fd706537bb9.png" alt="Reference architecture showing a user, an AI agent core, tools, a memory service, and an orchestration runtime" class="image--center mx-auto" width="1100" height="554" loading="lazy"></p>
<p><em>Figure 4 — Reference architecture: agent core + tools + memory service + orchestration runtime</em></p>
<p>The above diagram shows a high-level, personalized AI agent architecture. In it, an agent core handles reasoning and planning while interacting with a tool interface layer, a long-term memory service, and an orchestration runtime.</p>
<p>Let’s now understand the moving parts of a personalized agent and how they interact.</p>
<p>At a high level, the system has four responsibilities:</p>
<ol>
<li><p><strong>Reasoning</strong> – deciding what to do next</p>
</li>
<li><p><strong>Execution</strong> – calling tools and services</p>
</li>
<li><p><strong>Memory</strong> – storing and retrieving long-term preferences</p>
</li>
<li><p><strong>Boundaries</strong> – controlling what the agent is allowed to do</p>
</li>
</ol>
<p>A common mistake you’ll see is to blur these responsibilities together. For example, letting the model decide when to write memory, or allowing tools to execute actions without clear constraints.</p>
<p>Instead, you'll design the system so each responsibility has a clear owner. The core components look like this:</p>
<ul>
<li><p><strong>Agent core</strong>: Handles reasoning and planning</p>
</li>
<li><p><strong>Tools</strong>: Perform external actions (read or write)</p>
</li>
<li><p><strong>MCP layer</strong>: Defines how tools are exposed and invoked</p>
</li>
<li><p><strong>Memory services</strong>: Store long-term user data safely</p>
</li>
</ul>
<p>ADK sits at the center, orchestrating how requests flow between these components. The model never directly talks to databases or services. It reasons about actions, and ADK coordinates execution.</p>
<p>This separation makes the system easier to reason about, debug, and extend.</p>
<h2 id="heading-how-to-design-the-agent-core-with-adk"><strong>How to Design the Agent Core with ADK</strong></h2>
<p>Before we dive in, a quick note on what ADK is<strong>.</strong>  </p>
<p><strong>Agent Development Kit (ADK)</strong> is an agent orchestration framework – the glue code between a large language model and your application. Instead of treating the model as a black box that directly “does things”, ADK helps you structure the agent as a system:</p>
<ul>
<li><p>The model focuses on <strong>reasoning</strong> (turning user intent, context, and memory into a structured plan)</p>
</li>
<li><p>Your runtime stays in control of <strong>execution</strong> (deciding which tools can run, how they run, and what gets logged or persisted)</p>
</li>
</ul>
<p>In other words, ADK is what lets you take tool calling and multi-step workflows out of a giant prompt and turn them into a maintainable and testable architecture. In this tutorial, we’ll use ADK to refer to that orchestration layer. The same patterns apply if you use a different agent framework.</p>
<p><strong>Note:</strong> The following code snippets are simplified reference examples intended to illustrate architectural patterns. They’re not production-ready drop-ins.</p>
<p>Once you understand the architecture, you can start designing the agent core. The agent core is responsible for reasoning, not execution.</p>
<p>A helpful mental model is to think of the agent as a planner, not a doer. Its role is to interpret the user’s goal, consider available context and memory, and produce a structured plan that can later be executed in a controlled way.</p>
<p>To make this concrete, the following example shows how an agent can translate user input and memory into an explicit plan. In practice, ADK orchestrates this using a large language model, but the important idea is that the output is structured intent, not side effects.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Reference example for illustration.</span>

<span class="hljs-keyword">from</span> dataclasses <span class="hljs-keyword">import</span> dataclass
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List, Dict, Any

<span class="hljs-meta">@dataclass</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Step</span>:</span>
    tool: str
    args: Dict[str, Any]

<span class="hljs-meta">@dataclass</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Plan</span>:</span>
    goal: str
    steps: List[Step]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">build_plan</span>(<span class="hljs-params">user_text: str, memory: Dict[str, Any]</span>) -&gt; Plan:</span>
    <span class="hljs-comment"># In practice, the LLM produces this structure via ADK orchestration.</span>
    goal = <span class="hljs-string">f"Help user: <span class="hljs-subst">{user_text}</span>"</span>
    steps = []
    <span class="hljs-keyword">if</span> memory.get(<span class="hljs-string">"prefers_short_answers"</span>):
        steps.append(Step(tool=<span class="hljs-string">"set_style"</span>, args={<span class="hljs-string">"verbosity"</span>: <span class="hljs-string">"low"</span>}))
    steps.append(Step(tool=<span class="hljs-string">"search_docs"</span>, args={<span class="hljs-string">"query"</span>: user_text}))
    steps.append(Step(tool=<span class="hljs-string">"summarize"</span>, args={<span class="hljs-string">"max_bullets"</span>: <span class="hljs-number">5</span>}))
    <span class="hljs-keyword">return</span> Plan(goal=goal, steps=steps)
</code></pre>
<p>This example illustrates an important constraint: the agent produces a plan, but it doesn’t execute anything directly.</p>
<p>The agent decides <em>what</em> should happen and <em>in what order</em>, while ADK controls <em>when</em> and <em>how</em> each step runs. This separation lets you inspect, test, and reason about decisions before they result in real-world actions.</p>
<p>When personalization is involved, this distinction becomes critical. Preferences may influence planning, but execution should remain tightly controlled by the runtime.</p>
<p>Again, we can consider the agent to be a planner, not a doer.</p>
<p>It should not:</p>
<ul>
<li><p>Perform side effects directly</p>
</li>
<li><p>Write to databases</p>
</li>
<li><p>Call external APIs without supervision</p>
</li>
</ul>
<p>In ADK, this separation is natural. The agent produces intents and tool calls, while the runtime controls how and when those calls are executed.</p>
<p>This design has two major benefits:</p>
<ol>
<li><p><strong>Safety</strong> – you can restrict which tools the agent can access</p>
</li>
<li><p><strong>Debuggability</strong> – you can inspect decisions before execution</p>
</li>
</ol>
<p>When personalization is involved, this becomes even more important. Preferences influence reasoning, but execution should remain tightly controlled.</p>
<h2 id="heading-how-to-connect-tools-safely-with-mcp"><strong>How to Connect Tools Safely with MCP</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770578793149/2e3f8282-341a-4f03-9313-df3f8c9c5174.png" alt="Tool call routed through a control layer with request, validation, execution, and response steps." class="image--center mx-auto" width="1362" height="870" loading="lazy"></p>
<p><em>Figure 5 — Tool calls with guardrails: request → validate → execute → respond</em></p>
<p>Tools are how agents interact with the real world. They fetch data, generate artifacts, and sometimes perform actions with side effects.</p>
<p>Without clear boundaries, tool usage quickly becomes a source of fragility. Hardcoded API calls leak into prompts, tools evolve independently, and agents gain more authority than intended.</p>
<p>To avoid these problems, tools should be explicitly registered and invoked through a narrow interface. The following example shows a simple tool registry pattern that mirrors how MCP exposes tools to an agent without tightly coupling it to implementations.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Reference example (pseudocode for illustration)</span>

<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Callable, Dict, Any

ToolFn = Callable[[Dict[str, Any]], Dict[str, Any]]

TOOLS: Dict[str, ToolFn] = {}

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">register_tool</span>(<span class="hljs-params">name: str</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">decorator</span>(<span class="hljs-params">fn: ToolFn</span>):</span>
        TOOLS[name] = fn
        <span class="hljs-keyword">return</span> fn
    <span class="hljs-keyword">return</span> decorator

<span class="hljs-meta">@register_tool("search_docs")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search_docs</span>(<span class="hljs-params">args: Dict[str, Any]</span>) -&gt; Dict[str, Any]:</span>
    query = args[<span class="hljs-string">"query"</span>]
    <span class="hljs-comment"># Replace with your MCP client call (or local tool implementation).</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"results"</span>: [<span class="hljs-string">f"doc://example?q=<span class="hljs-subst">{query}</span>"</span>]}

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">invoke_tool</span>(<span class="hljs-params">name: str, args: Dict[str, Any]</span>) -&gt; Dict[str, Any]:</span>
    <span class="hljs-keyword">if</span> name <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> TOOLS:
        <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Tool not allowed: <span class="hljs-subst">{name}</span>"</span>)
    <span class="hljs-keyword">return</span> TOOLS[name](args)
</code></pre>
<p>The Model Context Protocol (MCP) provides a clean way to formalize this pattern. You can think of MCP the same way operating systems treat system calls.</p>
<p>An application does not directly manipulate hardware. Instead, it requests operations through well-defined system calls. The kernel decides whether the operation is allowed and how it executes.</p>
<p>In the same way, the agent knows <em>what</em> capabilities exist, MCP defines <em>how</em> those capabilities are invoked, and the runtime controls <em>when</em> and <em>whether</em> they execute.</p>
<p>This separation prevents several common problems, including hardcoded API details in prompts, unexpected breakage when tools change, and agents performing unrestricted side effects.</p>
<p>When designing tools, it helps to classify them by risk: read tools for safe queries, generate tools for planning or synthesis, and commit tools for irreversible actions. In a personalized agent, commit tools should be rare and tightly guarded.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770580271505/d5d34514-3b98-4997-85ed-dee55e65d711.png" alt="Observability around tool calls using logs, traces, and timing across decision points" class="image--center mx-auto" width="996" height="606" loading="lazy"></p>
<p><em>Figure 6 — Observability around tool calls: logs, traces, timing, decision points</em></p>
<h2 id="heading-how-to-add-long-term-memory-without-polluting-context"><strong>How to Add Long-Term Memory Without Polluting Context</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770577944241/b2a3de65-c5e2-456e-8a33-e9fd4d2695f0.png" alt="Memory candidates extracted from user input, filtered and validated, then stored asynchronously" class="image--center mx-auto" width="1118" height="478" loading="lazy"></p>
<p><em>Figure 7 — Memory admission pipeline: extract → filter/validate → persist asynchronously</em></p>
<p>Memory is where personalization either succeeds or fails.</p>
<p>You can start by storing everything the user says and feed it back into the prompt. This works briefly, then collapses under its own weight as context grows, costs rise, and behavior becomes unpredictable.</p>
<p>A better approach is to treat memory as structured, curated data so you can control what the agent remembers and why with clear admission rules. Before persisting anything, the system should explicitly decide whether the information is worth remembering. The following function demonstrates a simple memory admission policy.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Simplified Reference Only</span>
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Optional, Dict, Any

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">memory_candidate</span>(<span class="hljs-params">user_text: str</span>) -&gt; Optional[Dict[str, Any]]:</span>
    text = user_text.lower()

    <span class="hljs-comment"># Durable</span>
    <span class="hljs-keyword">if</span> <span class="hljs-string">"for this session"</span> <span class="hljs-keyword">in</span> text <span class="hljs-keyword">or</span> <span class="hljs-string">"ignore after"</span> <span class="hljs-keyword">in</span> text:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

    <span class="hljs-comment"># Reusable</span>
    <span class="hljs-keyword">if</span> <span class="hljs-string">"my preferred language is"</span> <span class="hljs-keyword">in</span> text:
        <span class="hljs-keyword">return</span> {<span class="hljs-string">"type"</span>: <span class="hljs-string">"preference"</span>, <span class="hljs-string">"key"</span>: <span class="hljs-string">"language"</span>, <span class="hljs-string">"value"</span>: user_text.split()[<span class="hljs-number">-1</span>]}

    <span class="hljs-comment"># Safe (basic example; add PII checks for your use case)</span>
    <span class="hljs-keyword">if</span> <span class="hljs-string">"password"</span> <span class="hljs-keyword">in</span> text <span class="hljs-keyword">or</span> <span class="hljs-string">"ssn"</span> <span class="hljs-keyword">in</span> text:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>  <span class="hljs-comment"># default: don’t store</span>
</code></pre>
<p>This policy encodes three questions every memory candidate must answer:</p>
<ul>
<li><p>Is it durable? Will it still matter in the future?</p>
</li>
<li><p>Is it reusable? Will it influence future decisions meaningfully?</p>
</li>
<li><p>Is it safe to persist? Does it avoid sensitive or session-specific data?</p>
</li>
</ul>
<p>Only information that passes all three checks should become long-term memory. In practice, this usually includes stable preferences and long-lived constraints, not temporary instructions or intermediate reasoning.</p>
<h3 id="heading-privacy-consent-and-lifecycle-controls-production-checklist"><strong>Privacy, Consent, and Lifecycle Controls (Production Checklist)</strong></h3>
<p>Even if your admission rules are solid, long-term memory introduces governance requirements:</p>
<ul>
<li><p><strong>User control:</strong> allow users to view, export, and delete stored preferences at any time.</p>
</li>
<li><p><strong>Sensitive data handling:</strong> never store secrets/PII. Run PII detection on every memory candidate (and consider redaction).</p>
</li>
<li><p><strong>Retention + consent:</strong> use explicit consent for persistent memory and apply retention windows (TTL) so memory expires unless it’s still useful.</p>
</li>
<li><p><strong>Security + auditability:</strong> encrypt at rest, restrict access by service identity, and keep an audit log of memory writes/updates.</p>
</li>
</ul>
<p>Memory writes should also be asynchronous. The agent should never block while persisting memory, which keeps interactions responsive and avoids coupling reasoning to storage latency.</p>
<h2 id="heading-how-the-end-to-end-agent-flow-works"><strong>How the End-to-End Agent Flow Works</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770578847727/f3cbc4b9-5bc9-4026-ae69-6fd7bc1625fc.png" alt="End-to-end flow showing user input, agent reasoning, tool invocation, and memory updates with feedback loops" class="image--center mx-auto" width="1134" height="308" loading="lazy"></p>
<p><em>Figure 8 — End-to-end request lifecycle: user input → plan → tools → memory updates</em></p>
<p>At this point, you can trace exactly how memory and tools interact during a single request. With the individual components in place, it’s helpful to see how they work together during a single request. The following example walks through the full lifecycle of a personalized interaction, from user input to response.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Reference example (pseudocode for illustration)</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_request</span>(<span class="hljs-params">user_id: str, user_text: str</span>) -&gt; str:</span>
    memory = memory_store.get(user_id)  <span class="hljs-comment"># e.g., {"prefers_short_answers": True}</span>
    plan = build_plan(user_text, memory)

    tool_outputs = []
    <span class="hljs-keyword">for</span> step <span class="hljs-keyword">in</span> plan.steps:
        out = invoke_tool(step.tool, step.args)
        tool_outputs.append({step.tool: out})

    response = render_response(goal=plan.goal, tool_outputs=tool_outputs, memory=memory)

    cand = memory_candidate(user_text)
    <span class="hljs-keyword">if</span> cand:
        <span class="hljs-comment"># Never block the user on storage.</span>
        memory_store.write_async(user_id, cand)
    <span class="hljs-keyword">return</span> response
</code></pre>
<p>At a high level, the flow looks like this:</p>
<ol>
<li><p>The user sends a message.</p>
</li>
<li><p>Relevant long-term memory is retrieved.</p>
</li>
<li><p>The agent reasons about the request and produces a plan.</p>
</li>
<li><p>ADK invokes tools through MCP as needed.</p>
</li>
<li><p>Results flow back to the agent.</p>
</li>
<li><p>The agent decides whether new information should be persisted.</p>
</li>
<li><p>Memory is written asynchronously.</p>
</li>
<li><p>The final response is returned to the user.</p>
</li>
</ol>
<p>Notice what does <strong>not</strong> happen: the model does not directly write memory, tools do not execute without coordination, and context does not grow without bounds. This structure keeps personalization controlled and predictable.</p>
<h2 id="heading-common-pitfalls-youll-hit-and-how-to-avoid-them"><strong>Common Pitfalls You’ll Hit (and How to Avoid Them)</strong></h2>
<p>Even with a solid architecture, there are a few failure modes that show up repeatedly in real systems. Many of them stem from allowing agents to perform irreversible actions without explicit checks.</p>
<p>The following example shows a simple guardrail for commit-style tools that require approval before execution.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Reference example (pseudocode for illustration)</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">invoke_commit_tool</span>(<span class="hljs-params">name: str, args: Dict[str, Any], approved: bool</span>) -&gt; Dict[str, Any]:</span>
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> approved:
        <span class="hljs-comment"># Require explicit confirmation or policy approval before side effects.</span>
        <span class="hljs-keyword">return</span> {<span class="hljs-string">"status"</span>: <span class="hljs-string">"blocked"</span>, <span class="hljs-string">"reason"</span>: <span class="hljs-string">"commit tools require approval"</span>}

    <span class="hljs-comment"># For example: create_ticket, send_email, submit_order, update_record</span>
    <span class="hljs-keyword">return</span> invoke_tool(name, args)
</code></pre>
<p>This pattern forces a clear decision point before side effects occur. It also creates an audit trail that explains <em>why</em> an action was allowed or blocked.</p>
<p>Other common pitfalls include over-personalization, leaky memory that persists session-specific data, uncontrolled tool growth, and debugging blind spots caused by unclear boundaries. If you see these symptoms, it usually means responsibilities are not clearly separated.</p>
<h2 id="heading-what-you-learned-and-where-to-go-next"><strong>What You Learned and Where to Go Next</strong></h2>
<p>Personalized AI agents are powerful, but they require discipline. The key insight is that personalization is a <strong>systems problem</strong>, not a prompt problem.</p>
<p>By separating reasoning from execution, structuring memory carefully, and using protocols like MCP to enforce boundaries, you can build agents that scale beyond demos and remain maintainable in production.</p>
<p>As you extend this system, resist the urge to add “just one more prompt tweak.” Instead, ask whether the change belongs in memory, tools, or orchestration.  </p>
<p>That mindset will save you time as your agent grows in complexity.  </p>
<p>If you’d like to continue the conversation, you can find me on <a target="_blank" href="https://www.linkedin.com/in/natarajsundar/">LinkedIn</a>.</p>
<p>*All diagrams in this article were created by the author for educational purposes.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ From Idea to Launch – The Ultimate Tool Stack for Indie Hackers ]]>
                </title>
                <description>
                    <![CDATA[ If you’re building something on your own, you don’t have time to mess around with bloated platforms or complicated setups.  You want tools that work, tools that feel intuitive, and tools that help you move quickly from idea to launch.  This article w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/ultimate-tool-stack-for-indie-hackers/</link>
                <guid isPermaLink="false">688271db426f0b23a6b69d43</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Indie Maker ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Thu, 24 Jul 2025 17:48:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753379224789/8cfaa6a0-1fe3-496f-841b-3d054f5479e5.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’re building something on your own, you don’t have time to mess around with bloated platforms or complicated setups. </p>
<p>You want tools that work, tools that feel intuitive, and tools that help you move quickly from idea to launch. </p>
<p>This article walks you through a tool stack that’s become popular with indie hackers. Whether you’re building a simple app, launching a SaaS, or testing a digital product, this stack will carry you from your first survey to your first sale.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-validate-your-idea-before-you-build">How to Validate Your Idea Before You Build</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tech-stack-for-indie-hackers">Tech Stack for Indie Hackers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hosting-for-indie-hackers">Hosting for Indie Hackers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-payment-solutions-for-indie-hackers">Payment Solutions for Indie Hackers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-analytics-for-indie-hackers">Analytics for Indie Hackers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-launch-where-your-people-are">Launch Where Your People Are</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-it-all-comes-together">How It All Comes Together</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-how-to-validate-your-idea-before-you-build"><strong>How to Validate Your Idea Before You Build</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753081777261/28c4d4c2-c78f-4b15-9306-ccf7e8283dd7.png" alt="How to Validate Your Idea Before You Build" class="image--center mx-auto" width="840" height="512" loading="lazy"></p>
<p>Before writing any code, you have to make sure people actually want what you’re about to build. This step is often skipped, but it’s where successful products are born. </p>
<p>You don’t need anything fancy here. <a target="_blank" href="http://forms.google.com/">Google Forms</a> is a perfect starting point. It’s free, fast, and easy to share. </p>
<p>You can ask simple questions like what tools people are currently using, what frustrates them, or whether they’d pay for a better solution. Keep your form short and clear, and send it out to communities like <a target="_blank" href="https://www.indiehackers.com/">Indie Hackers</a>, Reddit, or even your own network.</p>
<p>If you’re looking for something more polished, <a target="_blank" href="https://youform.com/">YouForm</a> is a great upgrade. It lets you create better-looking forms with more control over the design and branding. </p>
<p>Youform also gives you insights like where people drop off or how long they spend answering. That feedback can help you refine both your product and your message. </p>
<p>Whether you use Google Forms or YouForm, the goal is the same: find out if your idea has legs before you build anything.</p>
<h2 id="heading-tech-stack-for-indie-hackers">Tech Stack for Indie Hackers</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753081798778/105abceb-2953-451a-896e-70558947fbeb.png" alt="Tech Stack for Indie Hackers" class="image--center mx-auto" width="1200" height="675" loading="lazy"></p>
<p>Once you’ve validated the idea and know there’s interest, it’s time to build. </p>
<p>This is where many indie hackers hit a wall. The tech stack can make or break your momentum. You want something modern, fast, and flexible, without needing to stitch together a dozen services.</p>
<p><a target="_blank" href="https://nextjs.org/">Next.js</a> is one of the best choices for building apps and websites. It’s built on React, which means you can reuse components and move quickly. </p>
<p>But unlike plain React, Next.js also gives you routing, <a target="_blank" href="https://www.freecodecamp.org/news/server-side-rendering-javascript/">server-side rendering</a>, and static generation, all out of the box. That means you can use it for your landing page, dashboard, <a target="_blank" href="https://www.freecodecamp.org/news/how-to-deploy-a-nextjs-blog-on-sevalla/">a blog</a>, or your full app.</p>
<p>Now, you’ll need a backend. That’s where <a target="_blank" href="https://supabase.com/">Supabase</a> comes in. </p>
<p>Supabase gives you a fully managed PostgreSQL database, user authentication, file storage, and even real-time updates. It’s open source and developer-friendly. </p>
<p>You can sign up and have a working backend in minutes, without writing server code or managing infrastructure. It plays beautifully with Next.js, and for many projects, it gives you everything you need without ever spinning up your own server.</p>
<h2 id="heading-hosting-for-indie-hackers"><strong>Hosting for Indie Hackers</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753081812714/e72171f8-7381-4cd6-ad8c-279f3dc21cbd.jpeg" alt="Hosting for Indie Hackers" class="image--center mx-auto" width="1200" height="675" loading="lazy"></p>
<p>Once you’ve built your app, you need to get it online. Hosting used to be one of the most painful parts of building solo, but these days, many platforms make it feel easy and remove the need for a dedicated DevOps. </p>
<p>Tools like Vercel, Railway, and Sevalla let you connect your GitHub repo and deploy your app with a few clicks. They handle your databases, environment variables, deployments, and logs in a single dashboard. It’s a perfect fit for full-stack apps using Next.js and Supabase.</p>
<h2 id="heading-payment-solutions-for-indie-hackers"><strong>Payment Solutions for Indie Hackers</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753081837438/5c24aa9a-105f-4171-b55e-61e776b8f133.jpeg" alt="Payment Solutions for Indie Hackers" class="image--center mx-auto" width="1640" height="1140" loading="lazy"></p>
<p>Once you’re ready to charge for your product, you need a payment provider you can trust. </p>
<p><a target="_blank" href="https://stripe.com/">Stripe</a> is the industry standard. It supports one-time payments, subscriptions, and even complex pricing logic. It’s incredibly powerful – but it also has a bit of a learning curve. The setup process can feel like overkill if you’re just trying to see if someone will pay $5 for your tool.</p>
<p>That’s why many indie hackers start with <a target="_blank" href="https://www.lemonsqueezy.com/">LemonSqueezy</a>. It’s a checkout-first platform that handles VAT, tax compliance, licensing, and payments all in one. </p>
<p>You can set up a product, embed a buy button or link, and start collecting payments within a day. It’s built with creators in mind, which means fewer steps and more features tailored to solo projects. </p>
<p>If Stripe is the enterprise-grade option, LemonSqueezy is your lightweight indie companion.</p>
<h2 id="heading-analytics-for-indie-hackers"><strong>Analytics for Indie Hackers</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753081854623/8737aa5d-fa29-4afa-9aba-26953ede47cd.jpeg" alt="Analytics for Indie Hackers" class="image--center mx-auto" width="1300" height="733" loading="lazy"></p>
<p>Once you have traffic, users, and sales, it’s time to learn from them. This is where analytics come in. </p>
<p><a target="_blank" href="http://analytics.google.com/">Google Analytics</a> is the traditional choice. It’s free and feature-rich. You can track where people come from, what pages they visit, how long they stay, and more. But it’s also complex and a bit heavy, especially for smaller apps. If you just want quick insights, it can feel overwhelming.</p>
<p>That’s why tools like <a target="_blank" href="https://usefathom.com/">Fathom Analytics</a> are gaining popularity. Fathom gives you a clean, privacy-friendly dashboard with just the metrics that matter: page views, unique visits, and bounce rate. It’s <a target="_blank" href="https://gdpr.eu/">GDPR-compliant</a> and super easy to add to your site. You won’t get lost in charts or filters. You’ll exactly know what’s going on.</p>
<p>If you want to go even deeper, especially inside your product, <a target="_blank" href="https://posthog.com/">PostHog</a> is worth a look. It offers product analytics like event tracking, funnels, and even session recordings. </p>
<p>Want to know which button users click most? Or where do they drop off in your signup flow? PostHog can show you. It does take more setup, and you may want to self-host it, but for serious insights into user behaviour, it’s hard to beat.</p>
<h2 id="heading-launch-where-your-people-are"><strong>Launch Where Your People Are</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753081985314/22728ed8-e04a-466b-aca8-d25bdeac26a2.jpeg" alt="Launch Where Your People Are" class="image--center mx-auto" width="2000" height="1334" loading="lazy"></p>
<p>You’ve built it, hosted it, and maybe even started making money. Now you need to tell people about it. A strong launch can make all the difference.</p>
<p><a target="_blank" href="https://www.producthunt.com/">Product Hunt</a> is a great platform for tech-focused launches. If your product solves a real problem and looks good, it’s a great way to get attention. </p>
<p>Create a clean page with a short tagline, a few screenshots, and maybe a quick demo video. When you launch, stick around and reply to comments. Your first customers might come from there.</p>
<p><a target="_blank" href="https://www.indiehackers.com/article/how-to-launch-a-product-with-no-money-and-no-customers-5416bac05c">Indie Hackers</a> is more about the long game. It’s a community of makers sharing their journeys. You can post updates, ask questions, and get feedback throughout the building process. When you do launch, you’re launching to people who already know what you’ve been working on. That trust makes a big difference.</p>
<p><a target="_blank" href="https://www.reddit.com">Reddit</a> can work too, but it’s tricky. Each subreddit has its own rules, and users hate spam. The key is to be helpful. Don’t just drop a link, share what you learned, show what you built, and ask for feedback. If it feels like a conversation, not a pitch, people will respond.</p>
<h2 id="heading-how-it-all-comes-together">How It All Comes Together</h2>
<p>Let’s say you’re building a lightweight team dashboard called ManageBuddy. </p>
<p>You start by posting a quick survey with Google Forms to <a target="_blank" href="https://www.reddit.com/r/indiehackers/">r/SideProject</a> and <a target="_blank" href="https://www.reddit.com/r/indiehackers/">Indie Hackers</a>. Fifty people respond, and many say they’d pay for a tool like this.</p>
<p>You build your app using Next.js and Supabase. Supabase handles your database and authentication. You deploy to Railway, link up your GitHub repo, and go live. For payments, you pick LemonSqueezy since it’s quick and simple. You embed the checkout page into your app and start charging $10/month.</p>
<p>You add Fathom to your site to see how people are finding it. Later, you install PostHog to see which features they use most. When you’re ready to go big, you launch on Product Hunt. The next day, you share your story on Indie Hackers and Reddit. Feedback starts rolling in. People sign up. Some even pay.</p>
<p>In a few weeks, you’ve gone from idea to product, with users, sales, and feedback guiding your next steps.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>The best thing about this stack is that it doesn’t demand perfection. You don’t need to be an expert in every tool. Start small, validate your idea, and build something useful. </p>
<p>These tools – Google Forms, Next.js, Supabase, Railway, Stripe, Fathom, and so on – are here to help you get to your first version quickly.</p>
<p>Don’t get stuck trying to pick the “perfect” stack. What matters is progress. These tools lower the barrier so you can focus on solving a real problem and connecting with real users. Build something simple, launch it, and improve from there.</p>
<p>You don’t need a big team. You just need the right tools and the will to keep going. Happy hacking!</p>
<p>Hope you enjoyed this article. <a target="_blank" href="https://www.linkedin.com/in/manishmshiva">Connect with me</a> on LinkedIn or <a target="_blank" href="https://manishshivanandhan.com/">visit my website</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Top 5 Object Storage Tools for Developers ]]>
                </title>
                <description>
                    <![CDATA[ Choosing a storage solution is one of the most significant decisions a developer (or development team) needs to make when building a web or mobile application. As you can imagine, there are many different storage options.  In this article, we’ll brie... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/top-5-object-storage-tools-for-developers/</link>
                <guid isPermaLink="false">66bb5258e326dc37a9d68ed5</guid>
                
                    <category>
                        <![CDATA[ Cloud ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ storage ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ry Vee ]]>
                </dc:creator>
                <pubDate>Fri, 27 Nov 2020 14:40:38 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/11/jakob-owens-uX7UOpU-884-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Choosing a storage solution is one of the most significant decisions a developer (or development team) needs to make when building a web or mobile application.</p>
<p>As you can imagine, there are many different storage options. </p>
<p>In this article, we’ll briefly discuss two of the most used cloud solutions: <a target="_blank" href="https://www.ionos.com/digitalguide/server/know-how/what-is-block-storage/">block storage</a> (also known as SAN or storage area network) and <a target="_blank" href="https://lakefs.io/object-storage/">object storage</a>. After this, we will go through my top 5 suggested object storage solutions.</p>
<p>There is a third type of storage that’s commonly used: file system storage. However, this mechanism can also concur with SANs and object storage, so we won't go too deep into it.</p>
<h2 id="heading-what-is-block-storage">What is Block Storage?</h2>
<p>Block storage is a network of hard drives connected via fiber-optic network. This gives it an edge over copper cables due to the increased speed.</p>
<p>The reason it's called block storage is that each file in this system is divided into “blocks” of data stored in a disk. Sectors in the disk hold onto individual blocks of data, and these blocks, when combined, form the whole file.</p>
<p>So while there are advantages of using SAN, like high scalability, it is costly and can get incredibly complex as the network grows.</p>
<h2 id="heading-what-is-object-storage">What is Object Storage?</h2>
<p>The defining feature of object storage is that, instead of storing files as blocks, data is stored as <a target="_blank" href="https://techterms.com/definition/object">objects</a>. </p>
<p>Typically these objects will have more data attached to them than the blocks used for block storage. The objects often include: </p>
<ul>
<li>A blob which contains all the payload (i.e., image, video, text content)</li>
<li>Metadata, which tells us more about the file (timestamps, permissions, author, revision, and so on)</li>
<li>A universally unique ID (UUID)</li>
</ul>
<p>One major advantage of this type of storage is that objects are easily obtained and found because of their UUID. With block storage, there’s a specific hierarchy of files that a user goes through before getting the data they need, which can considerably slow down data retrieval.</p>
<p>Now that we have that out of the way, here’s a list of my top 5 object storage tools for developers:</p>
<h3 id="heading-amazon-aws-s3httpsawsamazoncoms3"><a target="_blank" href="https://aws.amazon.com/s3/">Amazon AWS S3</a></h3>
<p><img src="https://lh3.googleusercontent.com/EaFmG10ltwYNmeyrtQrW0Td1DMX189sUjdWs5KUssGlLBr--KFKUnlYcawhqmAQyJeyv2WBKIehdeyp_jEpLjdiv1GJo8mFP-EzLCMbNhx8wMQ6A6n_PHJL8is7i21qXrg" alt="Image" width="1354" height="669" loading="lazy"></p>
<p>S3 is one of the pioneers of object storage. It manages gigantic loads of data from all over the world across hundreds of industries.</p>
<p><strong>Features</strong>:</p>
<ul>
<li>High reliability and durability as it stores S3 objects in copies across multiple systems.</li>
<li>Allows you to manage costs through its S3 Storage Classes, which provides different rates depending on access patterns.</li>
<li>Provides the highest security and protection for your data.</li>
</ul>
<h3 id="heading-google-cloud-storage-gcshttpscloudgooglecomstorage"><a target="_blank" href="https://cloud.google.com/storage/">Google Cloud Storage (GCS)</a></h3>
<p><img src="https://lh5.googleusercontent.com/EjlExkD_Wo8Jg4-hLNzzS_rQM_1-0kpD5RiQQA7fYV1CQxlpczDyNzFraXJvpft1ujMwlQ0HJGpCoa50NSMYxS-gfp6IB9M0ULxf20-sHPEAVX3rExv60A0saQ1j5WJ0mA" alt="Image" width="1354" height="669" loading="lazy"></p>
<p>Google offers four different storage types for business levels of all sizes. When you’re moving data across each of those storage types, it will provide you with the data lifecycle. With this, you can manage how long data should be stored until it has to be deleted.</p>
<p><strong>Features</strong>:</p>
<ul>
<li>You don’t have a minimum object size.</li>
<li>You have access to storage locations all around the world.</li>
<li>Very high durability and low latency.</li>
<li>Data has redundancy across several geographic locations.</li>
</ul>
<h3 id="heading-lakefshttpslakefsio"><a target="_blank" href="https://lakefs.io/">LakeFS</a></h3>
<p><img src="https://lh5.googleusercontent.com/bcXj_Y_kRrMpi2AzKVSuNcXLXB1nM9q3DvYSFzLRUgq_kCoiR3XIwp4RCF8oCyZj0NsEdwYEhozemu6VlYKSZuEkN_Lboe5xLmVXLzGMBTZJOaZp5grKe_NKRgm6aUJ9GA" alt="Image" width="1354" height="669" loading="lazy"></p>
<p>LakeFS is an open-source tool that works with object storage <a target="_blank" href="https://aws.amazon.com/big-data/datalakes-and-analytics/what-is-a-data-lake/">data lakes</a>. Data lakes usually store files or blobs in raw format centrally through a repository.</p>
<p>Data lakes, on their own, are limited by the lack of frequent communication between entities. LakeFS solves this by using data versioning.</p>
<p><strong>Features:</strong></p>
<ul>
<li>Through S3 or GCS, it allows scaling up to Petabytes in size by using a system that mimics Git.</li>
<li>You can experiment as it provides you with a development environment with your data.</li>
<li>Since it uses a Git-like scheme, you can safely use new data in another branch without affecting the main branch. You can then, later on, merge it safely once each aspect of new data checks out (schema, etc.).</li>
</ul>
<h3 id="heading-miniiohttpsminio"><a target="_blank" href="https://min.io/">MiniIO</a></h3>
<p><img src="https://lh6.googleusercontent.com/dUASXaVGj9APcrVmK0EYEU4-tbs7eSjSasDfyDXLF6_lk2MgIG2aFLPA70y2sGa5WaTWgQRQHbzkCeT4cvCDg30_dgVJyBx0qzhnNvzNboJHTMb7htYdlS09FbVEiQvmrA" alt="Image" width="1354" height="669" loading="lazy"></p>
<p>MiniIO is another open-source solution. It utilizes the Amazon S3 API, which makes it perfect for high scale projects that require super strict security.</p>
<p><strong>Features:</strong></p>
<ul>
<li>It calls itself the world’s fastest object storage as it has a read/write speed of up to 183 GB.</li>
<li>It applies web scaling principles – a cluster can join forces with other clusters until it forms multiple data centers.</li>
<li>It’s Kubernetes friendly.</li>
<li>Because it's open source, users can improve and freely redistribute it.</li>
</ul>
<h3 id="heading-stackpathhttpswwwstackpathcomproductsobject-storagesourceaffiliateampirgwc1ampclickidxtbytz2dxxylthdwux0mo3qwukew1b2pyt54ua0"><a target="_blank" href="https://www.stackpath.com/products/object-storage/?source=affiliate&amp;irgwc=1&amp;clickid=Xtbytz2dXxyLTHDwUx0Mo3QWUkEw1B2PYT54UA0">Stackpath</a></h3>
<p><img src="https://lh3.googleusercontent.com/Jb-xzBULDvCI5nZyffRY4xIT8c1Qez_Ik86zlAa2N3hPjk_hBQC3fDGdPbg57bcf38UmEvEulXynAZpjcBn06Zwicqgtbzl6MzgTHs5wl4GwoGyPrtxaAkw1YG4GsZwauw" alt="Image" width="1354" height="669" loading="lazy"></p>
<p>StackPath offers both a Content-Delivery Network service, <a target="_blank" href="https://www.networkworld.com/article/3224893/what-is-edge-computing-and-how-it-s-changing-the-network.html">Edge Computing</a>, and an S3 compatible Object Storage. It touts itself as a cheaper option to Amazon S3 and other cloud providers.</p>
<p><strong>Features</strong>:</p>
<ul>
<li>It is six times faster than competing services, especially when combined with the CDN or the Edge Computing platform.</li>
<li>It is serverless, which means it needs no warmup.</li>
<li>It has 45 edge locations, which means your application is available worldwide with the same performance anywhere.</li>
</ul>
<h3 id="heading-in-closing">In Closing</h3>
<p>There you have it – a short list of the top object storage tools that you can use for your next web or mobile project. Object storage has indeed proven a great way to store data when scalability is the most significant consideration.</p>
<p>Thanks for reading this article! I hope you learned a thing or two about storage models, especially about object storage. Please feel free to connect with me on <a target="_blank" href="https://linkedin.com/in/rvvergara">LinkedIn</a> and <a target="_blank" href="https://twitter.com/coachryanv">Twitter</a>.  </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ AWS Machine Learning Tools: The Complete Guide ]]>
                </title>
                <description>
                    <![CDATA[ In this article, we will take a look the machine learning tools offered by AWS. We'll also understand the type of problems they try to solve for their customers. AWS Machine Learning comprises a rich set of tools that Amazon offers to help developers... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/aws-machine-learning-tools-the-complete-guide/</link>
                <guid isPermaLink="false">66d035b2871ae63f179f6b8f</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Tue, 06 Oct 2020 12:10:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/09/wall-7.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, we will take a look the machine learning tools offered by AWS. We'll also understand the type of problems they try to solve for their customers.</p>
<p><a target="_blank" href="https://aws.amazon.com/machine-learning/">AWS Machine Learning</a> comprises a rich set of tools that Amazon offers to help developers integrate machine learning models into their applications. </p>
<p>AWS also offers pre-trained models for use cases including computer vision, recommendation engines, and language translation.</p>
<p>Amazon currently offers 15 machine learning services on its platform. In this article, we will take a look at each one of them so we can understand the type of problem they are trying to solve for their customers.</p>
<h2 id="heading-sagemaker">SageMaker</h2>
<p><a target="_blank" href="https://aws.amazon.com/sagemaker/">Amazon SageMaker</a> helps you to take your machine learning models from concept to production in a fraction of time compared to traditional code-based approaches. </p>
<p>Sagemaker is a managed service and has the complete suite of tools you need to build, train, and deploy your machine learning models. They help you label your data, optimize your algorithms, and more. It's a complete solution for creating and deploying machine learning models.</p>
<p>SageMaker also has an autopilot option that will automatically process the data and run it through multiple algorithms. This helps developers find the best algorithm for their model without manually training and testing those models. </p>
<p>Sagemaker also comes with an integrated IDE and a sharable Jupyter notebook that you can use to collaborate with your team.</p>
<h2 id="heading-codeguru">CodeGuru</h2>
<p><a target="_blank" href="https://aws.amazon.com/codeguru/">Amazon CodeGuru</a> lets you automate your code reviews and performance optimizations for your application. </p>
<p>It can find issues like <a target="_blank" href="https://searchstorage.techtarget.com/definition/race-condition">race conditions</a>, resource leaks, and wasted CPU cycles. This helps you produce higher quality code by providing specific recommendations based on code context.</p>
<p>Codeguru’s algorithms are trained with codebases from Amazon’s projects. Right now, CodeGuru supports only Java applications, but you can expect the functionality to extend to other languages in the near future.</p>
<h2 id="heading-comprehend">Comprehend</h2>
<p><a target="_blank" href="https://aws.amazon.com/comprehend/">Amazon Comprehend</a> is a Natural Language Processing service from Amazon that uses machine learning to find valuable insights from textual data. </p>
<p>Comprehend can work with unstructured data like product reviews, customer emails, twitter tweets, and so on to help you draw conclusions (like overall audience sentiment).</p>
<p>It's also a fully managed service, meaning you can use pre-trained models to work with your data. Comprehend also has an additional service called Amazon Comprehend Medical that lets you work with medical documents to analyze medical conditions and dosages.</p>
<h2 id="heading-forecast">Forecast</h2>
<p><a target="_blank" href="https://aws.amazon.com/forecast/">Amazon Forecast</a> is used to build time-series prediction models using your existing datasets. </p>
<p>Forecast works great for use cases like predicting future business expenses, stock price prediction, and resource planning for organizations based on customer demand.</p>
<p>This service is also customizable and lets you build custom models on top of Amazon’s existing deep learning models. Like most machine learning tools in AWS, Forecast is also fully managed and can scale according to your business needs.</p>
<h2 id="heading-fraud-detector">Fraud Detector</h2>
<p><a target="_blank" href="https://aws.amazon.com/fraud-detector/">Amazon Fraud Detector</a> is another fully managed service that helps you detect fake registrations and fraudulent transactions. </p>
<p>Fraud detector can identify potentially fraudulent accounts and then helps you set up additional verification for those flagged accounts.</p>
<p>A fraud detector needs an existing dataset of labeled fraudulent transactions in order to train and understand the pattern of your customer behavior. It then uses this data to prevent further fraudulent transactions. </p>
<p>You can also configure custom authentication rules for guest logins and product trials.</p>
<h2 id="heading-kendra">Kendra</h2>
<p><a target="_blank" href="https://aws.amazon.com/kendra/">Amazon Kendra</a> is an AI-powered enterprise search engine that will help you deliver highly accurate search results based on customer queries. </p>
<p>You can use. Kendra to power search engines in your products that help your users find exactly what they're looking for.</p>
<p>Kendra can also be used to help customers find answers to specific problems while using your product without the need for additional customer support. </p>
<p>Kendra also supports natural language questions, delivering an even smoother experience for your customers.</p>
<h2 id="heading-lex">Lex</h2>
<p><a target="_blank" href="https://aws.amazon.com/lex/">Amazon Lex</a> lets you build conversational interfaces into your products. Lex offers <a target="_blank" href="https://en.wikipedia.org/wiki/Natural-language_understanding">Natural Language Understanding (NLU)</a> models that can understand conversational input from users and perform the right actions.</p>
<p>Lex can be used as a replacement for manual customer support to help filter common queries, and it can answer them automatically. It is also a fully managed service that scales automatically and uses a pay-as-you-use model.</p>
<h2 id="heading-personalize">Personalize</h2>
<p><a target="_blank" href="https://aws.amazon.com/personalize/">Amazon Personalize</a> lets you create custom recommendations for your customers based on their usage patterns. </p>
<p>While traditional recommendation engines can be used only to recommend products, Personalize lets you literally personalize every step of your customer’s user experience.</p>
<p>Personalize is a great tool to build product recommendations, custom search results based on queries and employ targeted marketing promotions.</p>
<h2 id="heading-polly">Polly</h2>
<p><a target="_blank" href="https://aws.amazon.com/polly/">Amazon Polly</a> helps you build speech-enabled products for your customers. Polly provides lifelike voice outputs across a variety of languages including Chinese, Korean and Japanese.</p>
<p>Polly is powered by deep learning algorithms that mimic a conversational style interface that can be used in narrations, telephony applications, and other applications.</p>
<h2 id="heading-rekognition">Rekognition</h2>
<p><a target="_blank" href="https://aws.amazon.com/rekognition/">Amazon Rekognition</a> is a computer vision solution from AWS that helps developers build applications that can recognize objects from images and videos. </p>
<p>In addition to automatic object recognition, you can customize Rekognition to pick out specific objects and scenes based on your own requirements.</p>
<p>Rekognition can be used in use cases like identifying manufacturing defects in products, spotting unauthorized personnel in an organization, scanning for inappropriate content in movies, and so on. It can also be used to analyze player movements in games for post-game analysis.</p>
<h2 id="heading-textract">Textract</h2>
<p><a target="_blank" href="https://aws.amazon.com/textract/">Amazon Textract</a> enables you to read data from scanned documents. The usual approach to digitizing paper documents is using manual data entry or <a target="_blank" href="https://en.wikipedia.org/wiki/Optical_character_recognition">OCRs</a> with custom configurations. </p>
<p>Textract makes it easier by automatically applying rules to documents and extracting valuable data along with components like forms and images within the document.</p>
<p>Textract is useful for processing loan applications, medical claims, and more. In addition to extracting data, these can be optimized for search using Textract. Documents that usually take months to process using manual methods can be processed in hours using AWS Textract.</p>
<h2 id="heading-transcribe">Transcribe</h2>
<p><a target="_blank" href="https://aws.amazon.com/transcribe/">Amazon Transcribe</a> lets you build speech to text services in your application. Transcribe is useful in building medical transcription services, streaming audio, generating subtitles for video recordings, and more.</p>
<p>Transcribe can also be used to convert customer calls into text and analyze them for improved customer service. Cataloging audio archives is another use case for AWS transcribe.</p>
<h2 id="heading-translate">Translate</h2>
<p><a target="_blank" href="https://aws.amazon.com/translate/">Amazon Translate</a> is a machine learning service similar to Google Translate. Translate can work with a variety of languages with high accuracy, which lets businesses customize their languages based on the the demographics of their audiences.</p>
<p>Translate is also designed to be more natural-sounding to customers since the context of the sentence is also taken into account. </p>
<p>Translate is also highly customizable so it can help improve the accuracy of translation when working with your brand names and unique words related to your business.</p>
<h2 id="heading-deeplens">DeepLens</h2>
<p><a target="_blank" href="https://aws.amazon.com/deeplens/">Amazon DeepLens</a> is a video camera with in-built deep learning capabilities that helps you build and test computer vision models in real-time. </p>
<p>DeepLens is fully programmable and can be used to test models like live object recognition, classifying birds/ animals, face detection, and so on.</p>
<p>The product is designed for developers getting started in machine learning. It helps them get a grasp of how their models will work in the real world. </p>
<p>DeepLens is also integrated with the AWS ecosystem and can be used with other AWS services like Lambda and Rekognition to extend its capabilities.</p>
<h2 id="heading-deepracer">DeepRacer</h2>
<p>If you are a fan of self-driving cars, <a target="_blank" href="https://aws.amazon.com/deepracer/">AWS DeepRacer</a> is a small autonomous race car designed by AWS that runs using machine learning. DeepRacer helps you test your reinforcement learning models using a physical track.</p>
<p>You can build reinforcement learning models using AWS SageMaker and test them instantly using DeepRacer. Amazon also gives you the opportunity to connect and compete with fellow racing enthusiasts by building virtual private race tracks.</p>
<h2 id="heading-summary">Summary</h2>
<p>With a solution to almost every machine learning problem, Amazon Machine Learning offers a rich set of tools for machine learning engineers to work with. </p>
<p>Amazon also adds new services every few months based on new use cases, making it one of the most dependable platforms where engineers can build AI solutions for their customers.</p>
<p><em>Loved this article?</em> <a target="_blank" href="http://tinyletter.com/manishmshiva"><strong><em>Join my Newsletter</em></strong></a> <em>and get a summary of my articles and videos every Monday.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Test and Play with Web APIs the Easy Way with Postman ]]>
                </title>
                <description>
                    <![CDATA[ In a world where static websites and apps increasingly depend on separately maintained APIs, it can be hard to figure out how they work by just playing around in the browser.  So how can we use Postman to both test our existing APIs and understand ho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-test-and-play-with-web-apis-the-easy-way-with-postman/</link>
                <guid isPermaLink="false">66b8e36e47e3b55b9fb6ee45</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ programing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ QA ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Quality Assurance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 30 Jun 2020 16:34:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/postman.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In a world where static websites and apps increasingly depend on separately maintained APIs, it can be hard to figure out how they work by just playing around in the browser. </p>
<p>So how can we use Postman to both test our existing APIs and understand how they work?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-postman">What is Postman?</a></li>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build-learn">What are we going to build / learn?</a></li>
<li><a class="post-section-overview" href="#heading-part-0-getting-set-up-with-postman">Part 0: Getting set up with Postman</a></li>
<li><a class="post-section-overview" href="#heading-part-1-an-introduction-to-postman">Part 1: An introduction to Postman</a></li>
<li><a class="post-section-overview" href="#heading-part-2-creating-a-new-postman-request-to-get-info-about-squirtle">Part 2: Creating a new Postman request to GET info about Squirtle</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/p/a02335e1-4f9a-453d-8916-db6b8419cf99/part-3-creating-a-collection-of-requests-in-postman-for-the-pok-api">Part 3: Creating a collection of requests in Postman for the PokéAPI</a></li>
<li><a class="post-section-overview" href="#heading-part-4-making-post-requests-with-postman-to-translate-sentences-to-sound-like-yoda">Part 4: Making POST requests with Postman to translate sentences to sound like Yoda</a></li>
<li><a class="post-section-overview" href="#heading-part-5-authenticating-requests-to-the-lord-of-the-rings-api-with-an-api-key">Part 5: Authenticating requests to the Lord of the Rings API with an API Key</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/KFuaybrXCdw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-postman">What is Postman?</h2>
<p><a target="_blank" href="https://www.postman.com/">Postman</a> is a tool teams can use to reliably test APIs using easy to use configurations. It comes stocked with features you would expect when dealing with APIs, including authentication, setting headers, customizing the payload, and a bunch more that help reduce the friction of using an API.</p>
<p>And it’s not just for testing. The beauty is that this can be used for many aspects of working with APIs for many different members of the team. Maybe a Project Manager wants to verify that things work or might find it easier to make a change straight with the API, or a QA Engineer needs to make sure everything still works, or a developer wants to actively make changes while working on the API itself.</p>
<p>The best part about it – Postman provides collaboration features. The free tier includes exporting and importing collections of saved API requests as well as creating shared links. If you're part of a team, they have paid tiers that allow you to sync up your collections to make sure everyone has the most recent and up to date collection.</p>
<h2 id="heading-what-are-we-going-to-build-learn">What are we going to build / learn?</h2>
<p>We’re going to walk through two different example APIs to cover the concepts of Postman.</p>
<p>First, we’ll walk through some simple HTTP requests with a <a target="_blank" href="https://pokeapi.co/">public API for Pokémon</a>.</p>
<p>We’ll then use the Yoda Translator API for one part to demonstrate how to make specific HTTP requests.</p>
<p>Once we understand how the basics work, we’ll use the <a target="_blank" href="https://the-one-api.herokuapp.com/">Lord of the Rings API</a> to learn how authentication works with APIs. For this, you’ll need to register for a free account for an API key.</p>
<h2 id="heading-part-0-getting-set-up-with-postman">Part 0: Getting set up with Postman</h2>
<p>Before we get started, you’ll need <a target="_blank" href="https://www.postman.com/downloads/">Postman</a> in order to follow along with this walkthrough. The good news, is Postman is available for free on Mac, Windows, and Linux, so you should be able to find a version that works for you.</p>
<p>Get Postman: <a target="_blank" href="https://www.postman.com/downloads/">https://www.postman.com/downloads/</a></p>
<p>Once downloaded, go through the standard installation instructions, open it up, and we should be ready to go!</p>
<h2 id="heading-part-1-an-introduction-to-postman">Part 1: An introduction to Postman</h2>
<p>The first time you open up Postman you’ll immediately be shown a launchpad with a bunch of options to get started.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-launchpad.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>It might seem a bit overwhelming, but let’s break down some of the key concepts that we’ll need to know.</p>
<h3 id="heading-requests">Requests</h3>
<p>A request is kind of what it sounds like, it’s a specific API request. This will be a single type of request, whether it’s a GET or POST to a specific endpoint. You’ll want to create new requests for each type of endpoint which will allow you to move between them when testing.</p>
<h3 id="heading-collections">Collections</h3>
<p>A collection is a group of requests. This is handy for organizing your requests into different groups. This could be as simple as two totally different APIs (ie. Twitter vs Slack) or it could be two different groups of APIs for a single API (ie. Twitter Tweets API vs Twitter Accounts API).</p>
<h3 id="heading-authorization">Authorization</h3>
<p>Authorization is how requests are authenticated with an API, whether by a person making a request or by a computer making that request on your behalf. This commonly comes in the form of an API key which can be a static value assigned to your account or dynamically generated with tools like <a target="_blank" href="https://oauth.net/">OAuth</a>.</p>
<h3 id="heading-environments">Environments</h3>
<p>Environments will allow you to configure your endpoints to use specific variables that make it easier to use the same endpoints between different environments. For instance, you might have the same <code>/profile</code> endpoint on both your production and development environments, but they have different domains. Environments lets you manage a single request with a variable domain.</p>
<h3 id="heading-workspaces">Workspaces</h3>
<p>We won’t go too far into workspaces in this post, but it allows you to manage and organize different sets of collections. Imagine if you want to use Postman for both work and a personal project, you might have a Work workspace as well as a Personal workspace.</p>
<p>For the purposes of this article, we’ll be covering Requests, Collections, and Authorization.</p>
<h2 id="heading-part-2-creating-a-new-postman-request-to-get-info-about-squirtle">Part 2: Creating a new Postman request to GET info about Squirtle</h2>
<p>Now that we have a better understanding of the different terminology, let’s actually create a request.</p>
<p>At the top left of the UI you should see a but orange button that says <strong>New</strong>. Go ahead and click that and then select <strong>Request</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-create-new-request.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Before we get into the request itself, it requests a few things.</p>
<p>This first thing requires is a name. We’re going to start off by requesting information about the Pokémon Squirtle, so let’s name this “Pokémon - Squirtle”.</p>
<p>It also requires a collection, so click <strong>Create Collection</strong> and let’s name the collection “My Favorite Pokémon”.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-configure-new-request.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click the orange checkmark button next to the collection name then hit <strong>Save</strong>.</p>
<p>At this point we’ll have a new request, so let’s build that request.</p>
<p>There are two things we’ll first need to fill out for our first request:</p>
<ul>
<li><strong>Request type:</strong> GET, POST, PUT, etc - we’ll use GET</li>
<li><strong>Request URL:</strong> The endpoint for your API request - for our request we’ll use <a target="_blank" href="https://pokeapi.co/api/v2/pokemon/squirtle/">https://pokeapi.co/api/v2/pokemon/squirtle/</a></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-squirtle-pokemon-get.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And once you make sure those are correct, you can simply hit the blue <strong>Send</strong> button on the right and we’ve successfully made our first request!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-squirtle-pokemon-get-success.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We immediately get a few things we can see:</p>
<ul>
<li><strong>Body:</strong> at the bottom we should now see the response body of the API request. For our Squirtle API, we should have a JSON object with data like <code>abilities</code>, <code>base_experience</code>, and <code>forms</code>.</li>
<li><strong>Status:</strong> on the right, we should see the HTTP status code. “200 Ok” is a good sign and it means it was successful!</li>
<li><strong>Time:</strong> simply how long the request took to finish</li>
<li><strong>Size:</strong> the size in KB (in our example) of the response data</li>
</ul>
<p>You can also hover over Status, Time, and Size and get a more in depth look at each option.</p>
<p>So we made our first request!</p>
<p>Once thing to notice before we move on is that our request looks like it’s in a browser tab. If we’re done with that particular request, we can close the tab and click <strong>Save</strong> to make sure all of our changes are there for next time!</p>
<h2 id="heading-part-3-creating-a-collection-of-requests-in-postman-for-the-pokeapi">Part 3: Creating a collection of requests in Postman for the PokéAPI</h2>
<p>Now that we’ve created a request, let’s create a collection of them. Technically we already had to create a new collection for Part 2, but we’ll create a new one to learn how collections themselves work.</p>
<p>At the top left of the UI, click the orange <strong>New</strong> button again and select <strong>Collection</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-create-new-collection.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Similar to a request, it asks for a name so let’s call this “PokéAPI”. Optionally you can add a description, then click <strong>Create</strong> at the bottom.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-configure-new-collection.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On the left, you’ll now see your collection. You can select and expand the folder since we’ll be working with it.</p>
<p>Before we add a request, the PokéAPI has different types of requests, so it makes sense to organize it a little more thoroughly. So let’s click the three dots next to the PokéAPI collection and select <strong>Add Folder</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-add-folder-to-collection.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Similar to the others, this asks for a name. Folders are kind of like collections inside of a collection, so you get similar options. Let’s name this one “Pokémon” and click the orange <strong>Save</strong> button like before.</p>
<p>Now let’s add our requests! First, click the three dots next to the Pokémon folder, similar to how we added a folder to the collection, but this time select <strong>Add Request</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-add-request-to-collection.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Let’s name this request “Pokemon”. While it might be confusing that we have a Pokemon request inside of the Pokémon folder, Pokemon is just one of the endpoints of the Pokémon group.</p>
<p>Now, let’s use the same exact API that we used with our Squirtle request before:</p>
<ul>
<li><strong>Request Type:</strong> GET</li>
<li><strong>Request URL:</strong> <a target="_blank" href="https://pokeapi.co/api/v2/pokemon/squirtle/">https://pokeapi.co/api/v2/pokemon/squirtle/</a></li>
</ul>
<p>And similar to before, when we hit the blue <strong>Send</strong> button, we should see a successful request!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-successful-get-request-squirtle.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now let’s add another request. Follow the same process as before to create a new request under the PokéAPI Pokémon folder and let’s name this request “Abilities”.</p>
<p>If you scroll through the response from the first Squirtle endpoint, you see a lot of other API urls. At the top, we have <code>abilities</code> and we have two different ones — “torrent” and “rain-dish”.</p>
<p>Choose your favorite Squirtle ability and copy the <code>url</code> value into the new Abilities request we just created, I’m going to use <code>rain-dish</code>.</p>
<p>We can leave the Request Type as GET, hit the blue <strong>Send</strong> button, and we can again see a successful response!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-successful-request-squirtle-abilities.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here we get a lot of information about our Squirtle ability Rain Dish and some of the details come in different languages which is cool!</p>
<p>So now we have a new PokéAPI collection with a Pokémon folder representing the group of Pokémon API endpoints including Pokemon and abilities.</p>
<p>We’re going to stop Part 3 with those 2 requests, but feel free to continue on and add as many of the PokéAPI requests as you’d like!</p>
<h2 id="heading-part-4-making-post-requests-with-postman-to-translate-sentences-to-sound-like-yoda">Part 4: Making POST requests with Postman to translate sentences to sound like Yoda</h2>
<p>So far we’ve only made GET requests, but what if we wanted to make a POST request where we need to actually send some data?</p>
<p>For making a POST request, we’re going to use the Yoda Translator API from funtranslations.com. While this API only takes a single parameter, it’s still a good public endpoint we can use to understand the concept.</p>
<p>First, let’s create a new collection with a new request:</p>
<ul>
<li><strong>Collection:</strong> Fun Translations</li>
<li><strong>Request:</strong> Yoda</li>
</ul>
<p>This time, instead of a GET request, our request configuration will be:</p>
<ul>
<li><strong>Request Type:</strong> POST</li>
<li><strong>Request URL:</strong> <a target="_blank" href="https://api.funtranslations.com/translate/yoda">https://api.funtranslations.com/translate/yoda</a></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-new-request-yoda-api.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now this time, if we hit the blue <strong>Send</strong> button, we’ll notice we don’t get a successful 200 response, we get a 400!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-yoda-api-bad-request.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We never actually set up any data to be posted to the API and it requires that data, so let’s add it.</p>
<p>Right below the <strong>Request URL</strong>, click <strong>Body</strong>. Then instead of none, select <strong>raw</strong> as the body type. Finally, on the far right of the types, change <strong>Text</strong> to <strong>JSON</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-yoda-request-body-type.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then, in the space below it, you can add the following:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"text"</span>: <span class="hljs-string">"Hello, I am learning how to test APIs with Postman!"</span>
}
</code></pre>
<p>And now click the blue <strong>Send</strong> button again and we get a successful response!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-successful-post-body-yoda-api.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We can apply this concept to pretty much any API. Postman doesn’t only permit you to post JSON, it allows you to use the other formats that we see listed in the Body Type section, meaning you have a lot of options depending on what the API you’re using requires.</p>
<h2 id="heading-part-5-authenticating-requests-to-the-lord-of-the-rings-api-with-an-api-key">Part 5: Authenticating requests to the Lord of the Rings API with an API Key</h2>
<p>For the rest of the walkthrough, we’re going to use the Lord of the Rings API.</p>
<p>First up, the Lord of the Rings API requires authentication in order to make requests using an API key. So to start, you’ll before we dive in, you’ll need to go <a target="_blank" href="https://the-one-api.herokuapp.com/sign-up">create a free account</a>.</p>
<p><a target="_blank" href="https://the-one-api.herokuapp.com/sign-up">https://the-one-api.herokuapp.com/sign-up</a></p>
<p>Once you sign up and log in, the first thing you’ll see is your API key! Either copy this key down or remember where you can find it for later. If you leave the page, you can always grab it by navigating to <strong>Welcome</strong> and then <strong>Account</strong> in the navigation of the API website.</p>
<p>To get started, let’s first create a new collection and request:</p>
<ul>
<li><strong>Collection:</strong> Lord of the Rings</li>
<li><strong>Folder:</strong> Movie</li>
<li><strong>Request:</strong> All Movies</li>
<li><strong>Request Type:</strong> GET</li>
<li><strong>Request URL:</strong> <a target="_blank" href="https://the-one-api.herokuapp.com/v1/movie">https://the-one-api.herokuapp.com/v1/movie</a></li>
</ul>
<p>Once you’re set with the above, click <strong>Send</strong>, and you’ll notice immediately it gives a response that says 401 and that it’s unauthenticated.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-unauthorized-request-lord-of-the-rings-api.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Because this API requires the API key, this is exactly what we expected. So let’s click on the <strong>Authorization</strong> tab. We can then select a <strong>Type</strong> of <strong>Bearer Token</strong>, and on the right, we can paste in our key that we just set up with the Lord of the Rings API.</p>
<p>And as soon as we hit <strong>Send</strong>, we now see a successful response!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-authorized-successful-lord-of-the-rings-api-request.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This worked really great, but what if we have a bunch of requests that use a single key. Do we have to manage that on each request?</p>
<p>Instead of managing it on each individual request, we can manage it on the collection. Let’s first build another request.</p>
<p>Under our Lord of the Rings collection and in the Movie folder, create a new request:</p>
<ul>
<li><strong>Request:</strong> Quote by Movie ID</li>
<li><strong>Request Type:</strong> GET</li>
<li><strong>Request URL:</strong> <a target="_blank" href="https://the-one-api.herokuapp.com/v1/movie/%7Bid%7D">https://the-one-api.herokuapp.com/v1/movie/{id}</a></li>
</ul>
<p>In this request, let’s use an ID from the response of the first request, I’m going to use <code>5cd95395de30eff6ebccde5b</code> which is the ID of The Two Towers, so the request URL will look like:</p>
<pre><code>https:<span class="hljs-comment">//the-one-api.herokuapp.com/v1/movie/5cd95395de30eff6ebccde5b</span>
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-inherit-authorization-from-parent.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, instead of setting our token in the request Authorization, we’re going to leave the type as <strong>Inherit auth from parent</strong>. Click on the three dots next to the collection and select <strong>Edit</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-edit-collection.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here, we’re going to do the same exact thing we did with the first request but on the Collection configuration. Select the <strong>Authorization</strong> tab, under type select <strong>Bearer Token</strong>, and in the <strong>Token</strong> field again paste your token.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-add-authorization-api-key-token-to-collection.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Finally, click <strong>Update</strong> and hit the blue <strong>Send</strong> button again and we can see a successful request!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-authorized-request-inherit-token-from-parent.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We can now go back to our All Movies request and update the Authorization to use a Type of Inherit auth from parent and it should still continue to work!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/postman-successful-request-lord-of-the-rings-api.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-what-else-can-we-do-with-postman">What else can we do with Postman?</h2>
<p>While I covered a lot of the basics, there’s quite a lot more you can do with Postman. Here are a few of my favorites.</p>
<h3 id="heading-environment-variables">Environment Variables</h3>
<p>If you’re working as a developer on a project, it’s likely that your team uses multiple environments, such as a development and production environment. Instead of creating and maintaining completely separate requests, you can add an environment variable and instead change that variable when switching between environments!</p>
<p>Variables apply to many scenarios, but that’s a common use. Check out Postman’s docs to learn how.</p>
<p><a target="_blank" href="https://learning.postman.com/docs/postman/variables-and-environments/variables/">https://learning.postman.com/docs/postman/variables-and-environments/variables/</a></p>
<h3 id="heading-import-and-export-collections-and-data">Import and Export Collections and Data</h3>
<p>A great thing about Postman is once you have your requests all organized, you can export them for others to use. This also means that you can import collections from other team members. This makes it much easier to make sure everyone’s using the same collection.</p>
<p>Bonus: you can even store these files in a Git repository, as they’re just JSON.</p>
<p>But keep in mind - if you’re using Authorization on the collection like we went over in this guide, you’ll want to make sure you don’t include that when exporting your collection.</p>
<p><a target="_blank" href="https://learning.postman.com/docs/postman/collections/importing-and-exporting-data/">https://learning.postman.com/docs/postman/collections/importing-and-exporting-data/</a></p>
<h3 id="heading-automated-testing">Automated testing</h3>
<p>Once you have a set of requests in a collection and even better, if you’re storing them in Github, you can begin to use those requests as part of a way to manage automated testing of your API.</p>
<p>While there are a few solutions for doing this, Postman includes a Collection runner built right into the app and <a target="_blank" href="https://learning.postman.com/docs/postman/collection-runs/command-line-integration-with-newman/">Newman</a> is a command line tool that lets you run tests right from the terminal.</p>
<p><a target="_blank" href="https://www.postman.com/use-cases/api-testing-automation/">https://www.postman.com/use-cases/api-testing-automation/</a></p>
<h2 id="heading-whats-your-favorite-way-to-test-and-play-with-apis">What’s your favorite way to test and play with APIs?</h2>
<p><a target="_blank" href="https://twitter.com/colbyfayock">Share with me on Twitter!</a></p>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Sync VS Code Settings Between Multiple Devices and Environments ]]>
                </title>
                <description>
                    <![CDATA[ All developers like their text editor a certain way, but it can be tough to make sure all of your environments have the same configuration. How can we make sure our VS Code configuration is the same wherever we use it? What is VS Code? What will we ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-sync-vs-code-settings-between-multiple-devices-and-environments/</link>
                <guid isPermaLink="false">66b8e36c682e4a25eed261a2</guid>
                
                    <category>
                        <![CDATA[ code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ editor ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ programing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Visual Studio Code ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 16 Jun 2020 14:45:45 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>All developers like their text editor a certain way, but it can be tough to make sure all of your environments have the same configuration. How can we make sure our VS Code configuration is the same wherever we use it?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-vs-code">What is VS Code?</a></li>
<li><a class="post-section-overview" href="#heading-what-will-we-use">What will we use?</a></li>
<li><a class="post-section-overview" href="#heading-how-does-it-work">How does it work?</a></li>
<li><a class="post-section-overview" href="#heading-step-1-install-settings-sync">Step 1: Install Settings Sync</a></li>
<li><a class="post-section-overview" href="#heading-step-2-authorize-access-to-github">Step 2: Authorize access to Github</a></li>
<li><a class="post-section-overview" href="#heading-step-3-upload-your-current-settings">Step 3: Upload your current settings</a></li>
<li><a class="post-section-overview" href="#heading-step-4-syncing-your-configuration-to-another-environment">Step 4: Syncing your configuration to another environment</a></li>
<li><a class="post-section-overview" href="#heading-step-5-updating-your-configuration">Step 5: Updating your configuration</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/TR2va67cVkQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-vs-code">What is VS Code?</h2>
<p><a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a>, or VS Code, is an all-inclusive code editor that takes all of the features you want out of working with code and puts them in one editor to make you ultra productive.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/visual-studio-code-editor.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>VS Code editor</em></p>
<p>It’s been the “cool kid on the block” for a little while now and has been increasingly growing in popularity at least in the JavaScript community. Microsoft has put a lot of effort into making it something people want to use (and they’re doing a great job at that).</p>
<h2 id="heading-what-will-we-use">What will we use?</h2>
<p>We’re going to use a VS Code extension called <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync">Settings Sync</a> that makes use of Github’s <a target="_blank" href="https://gist.github.com/">Gist</a> feature to store a private JSON configuration file in the cloud.</p>
<h2 id="heading-how-does-it-work">How does it work?</h2>
<p>The extension uses Github’s OAuth to log in to your Github account. Once approved, VS Code obtains an access token and reaches out to both store and download your settings file to a private Github Gist.</p>
<p>Once it’s set up, you can then configure the extension on any other instance of VS Code and immediately download your configuration to sync up your editor.</p>
<h2 id="heading-step-0-vs-code">Step 0: VS Code</h2>
<p>We’ll assume for this walkthrough you have VS Code already installed. While you don’t need to have any special configuration, having something different than the default (like a <a target="_blank" href="https://code.visualstudio.com/docs/getstarted/themes">color theme</a>) will help you see it work.</p>
<p>Let’s get started!</p>
<h2 id="heading-step-1-install-settings-sync">Step 1: Install Settings Sync</h2>
<p>The first thing we need to do is install the extension. You can do this <a target="_blank" href="https://code.visualstudio.com/docs/editor/extension-gallery">a few ways</a> — you can visit <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync">the webpage</a> and hit <strong>Install</strong> which will open up VS Code or you can search for the extension in the Extensions panel.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>VS Code Settings Sync extension</em></p>
<p>Once installed, it will open up a new tab with the Settings Sync dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-dashboard.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Settings Sync dashboard after installation</em></p>
<h2 id="heading-step-2-authorize-access-to-github">Step 2: Authorize access to Github</h2>
<p>To get started with Github, click the <strong>Login with Github</strong> button in the Settings Sync dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-login-with-github.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Log in to Settings Sync with Github</em></p>
<p>This will open up Github in your default web browser and ask you to log in. While you can use any Github account you want, it would probably make most sense to use your personal account.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-login-success.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Successful Github login to Settings Sync</em></p>
<p>Once you’re logged in, you should now see <strong>Success!</strong> in your browser.</p>
<h2 id="heading-step-3-upload-your-current-settings">Step 3: Upload your current settings</h2>
<p>Now that you’re connected to Github, you’re ready to upload your settings.</p>
<p>Open up your <a target="_blank" href="https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette">Command Palette</a> with CMD+Shift+P (on Mac) or navigate to View and Command Palette. Type “Sync Upload” which will filter the commands and hit enter once the <strong>Sync: Update/Upload Settings</strong> option is selected.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-upload-update-command.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Update/Upload Settings command in Settings Sync</em></p>
<p>When doing this, you might be prompted with screen that asks if you want to force upload — press <strong>Yes</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-force-upload.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Force upload new settings in Settings Sync</em></p>
<p>At this point, Settings Sync will create a new Gist in your Github account with your configuration settings. Once it’s done you should see a success message.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-successful-upload.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Successful settings upload in Settings Sync</em></p>
<p>You should now be able to visit <a target="_blank" href="https://gist.github.com/">gist.github.com</a> and find a new private <code>cloudSettings</code> Gist that includes all of your VS Code settings!</p>
<h2 id="heading-step-4-syncing-your-configuration-to-another-environment">Step 4: Syncing your configuration to another environment</h2>
<p>To sync your VS Code configuration to another computer or VS Code environment, you want to first follow steps 1 and 2 above — installing the extension and logging in to Github.</p>
<p>The difference is this time, you’ll want to configure VS Code to download your settings instead of upload them.</p>
<p>To get started, first open back up your Sync Settings dashboard. If this is a new installation like we’re assuming here, you can open up the Command Palette and type “sync download” and hit enter which will open up that dashboard. Here, click <strong>Edit Configuration</strong> this time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-edit-configuration.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Edit Settings Sync configuration</em></p>
<p>On this screen, you should see your <strong>Github Access Token</strong>, but you should also see an empty field for Gist ID. Here, we want to first grab the ID from our cloudSettings Gist URL:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-github-gist.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>VS Code cloudSettings Gist ID</em></p>
<p>And then paste that value inside of our <strong>Gist ID</strong> input in VS Code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-gist-id-configuration-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding Gist ID to Settings Sync configuration</em></p>
<p>Once it’s there, you can open the Command Palette again, type “sync download”, and hit enter, and Sync Settings will fetch your VS Code configuration from the Gist and update your local settings with that configuration!</p>
<h2 id="heading-step-5-updating-your-configuration">Step 5: Updating your configuration</h2>
<p>From here on, any time you want to make a new change to your stored configuration, you’ll want to use both the Update/Upload and Download features like we used above.</p>
<p>To update a new tweak to your configuration, type “sync update”  and hit enter:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-update-settings.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Update command for Settings Sync</em></p>
<p>And to download your configuration to sync up another editor, type “sync download” and hit enter:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/vs-code-settings-sync-download-command.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Download settings command for Settings Sync</em></p>
<p>These commands will both update your cloudSettings Gist and download from it to sync up your VS Code instances</p>
<h2 id="heading-whats-your-favorite-vs-code-trick">What’s your favorite VS Code trick?</h2>
<p><a target="_blank" href="https://twitter.com/colbyfayock">Share it with me on Twitter!</a></p>
<h2 id="heading-join-the-conversation">Join the conversation</h2>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1272906851005366274"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Storybook and How Can I Use It to Create a Component Library in React? ]]>
                </title>
                <description>
                    <![CDATA[ Frameworks like React, Vue, and Angular all help developers create modular systems using components, but that doesn't usually include a good way to see them all from a higher point of view.  So how can we use Storybook to build libraries and design s... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-storybook-and-how-can-i-use-it-to-create-a-component-libary-in-react/</link>
                <guid isPermaLink="false">66b8e39c68c5b9f37d1d1af1</guid>
                
                    <category>
                        <![CDATA[ components ]]>
                    </category>
                
                    <category>
                        <![CDATA[ create-react-app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Design Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Libraries ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Storybook ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 09 Jun 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/storybook.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Frameworks like React, Vue, and Angular all help developers create modular systems using components, but that doesn't usually include a good way to see them all from a higher point of view. </p>
<p>So how can we use Storybook to build libraries and design systems that self-document as we build them?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-storybook">What is Storybook?</a></li>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-step-0-bootstrapping-an-app">Step 0: Bootstrapping an app</a></li>
<li><a class="post-section-overview" href="#heading-step-1-installing-storybook">Step 1: Installing Storybook</a></li>
<li><a class="post-section-overview" href="#heading-step-2-creating-a-new-button">Step 2: Creating a new button</a></li>
<li><a class="post-section-overview" href="#heading-step-3-using-our-new-button-component">Step 3: Using our new Button component</a></li>
<li><a class="post-section-overview" href="#heading-repeat-creating-a-new-header-component">Repeat: Creating a new Header component</a></li>
<li><a class="post-section-overview" href="#heading-more-storybook-features">More Storybook features</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VApXDsYO5Gg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-storybook">What is Storybook?</h2>
<p><a target="_blank" href="https://storybook.js.org/">Storybook</a> is a JavaScript tool that allows developers to create organized UI systems making both the building process and documentation more efficient and easier to use.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/loneley-planet-storybook-example.jpg" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://lonelyplanet.github.io/backpack-ui/?path=/story/cards--card-basic">Lonely Planet's Backpack UI</a></em></p>
<p>Once you build out a component, Storybook lets you create a "story" file where you can then import your component and create various use case examples in an iFramed sandbox using that component.</p>
<p>This provides an organized and focused environment to build new components and work on existing ones.</p>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We're going to bootstrap a new <a target="_blank" href="https://reactjs.org/">React JS</a> app using <a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">Create React App</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/storybook-component-example.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Inside that app, we're going to install Storybook and create a few new components that will help us learn how to create new components that we can work on in a story and then use it in a React app.</p>
<h2 id="heading-step-0-bootstrapping-an-app">Step 0: Bootstrapping an app</h2>
<p>To get started, we're going to start from scratch with <a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">Create React App</a>. This will help us focus on getting productive in Storybook rather than walking through integrating it into a current app.</p>
<p>That said, if you're already working with an app created using Create React App that's not ejected, you should be able to still follow on with Part 1 and beyond just the same!</p>
<p>So let's get started by navigating to where we want to create our new app and run the Create React App command:</p>
<pre><code class="lang-shell">npx create-react-app my-storybook
</code></pre>
<p><em>Note: feel free to replace <code>my-storybook</code> with the directory name of your choice.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/create-new-react-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Bootstrapping with Create React App</em></p>
<p>Once that's finished running, you can navigate to the directory:</p>
<pre><code class="lang-shell">cd my-storybook
</code></pre>
<p>And we're ready to go!</p>
<h2 id="heading-step-1-installing-storybook">Step 1: Installing Storybook</h2>
<p>Storybook luckily makes it really easy to get started with a standard installation of React. Particularly with Create React App, Storybook automatically detects that we're using an app created using CRA and installs the dependencies and scaffolds everything for us.</p>
<h3 id="heading-initializing-storybook">Initializing Storybook</h3>
<p>To get started installing Storybook, run:</p>
<pre><code class="lang-shell">npx -p @storybook/cli sb init
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/initializing-storybook.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Initializing Storybook in a React app</em></p>
<p>If you aren't using Create React App or it didn't work, you can check out their <a target="_blank" href="https://storybook.js.org/docs/guides/guide-react/">available guides in their docs</a>.</p>
<p>After that's finished, all of our Storybook dependencies should be installed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/storybook-finished-installing.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Finished installing Storybook</em></p>
<h3 id="heading-starting-up-storybook">Starting up Storybook</h3>
<p>So now we're ready to get moving! Finally, run:</p>
<pre><code class="lang-shell">yarn storybook
# or
npm run storybook
</code></pre>
<p>And once everything finishes loading, Storybook will open a new tab in your browser and you should now see a welcome message inside of your new Storybook dashboard!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/storybook-welcome-page.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Storybook welcome page</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-storybook/commit/3e994096384e31cb540150c9f14f41758ef3a746">Follow along with the commit!</a></p>
<h2 id="heading-step-2-creating-a-new-button">Step 2: Creating a new button</h2>
<p>If you took a second to poke around the dashboard, you might have noticed that it comes pre-loaded with a Button that's available as a demo.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/storybook-demo-button.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Storybook demo button</em></p>
<p>You should also notice if you click the button, you actually see an action print out inside of the Actions tab at the bottom. This shows the event that's captured from the button click.</p>
<p>It's simple, but this is great to get a nice feel about what to expect in storybook. The only issue is, this is meant purely for demonstration purposes, so let's build our own button to replace it.</p>
<h3 id="heading-creating-a-new-button-component">Creating a new Button component</h3>
<p>To get started, let's first create a few directories:</p>
<ul>
<li>Under <code>src</code>, create a new folder called <code>components</code></li>
<li>Under <code>components</code>, create a new folder called <code>Button</code></li>
</ul>
<p>Once you create those folders, create a new file called <code>index.js</code> inside of your <code>src/components/Button</code> folder and inside add:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Inside src/components/Button/index.js</span>

<span class="hljs-keyword">export</span> { <span class="hljs-keyword">default</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'./Button'</span>;
</code></pre>
<p>This will import the next file we created called <code>Button.js</code> which will allow us to more easily import our files with <code>src/components/Button</code> instead of <code>/src/components/Button/Button</code>.</p>
<p>Next, let's create <code>Button.js</code> right next to our <code>index.js</code> file with the following content:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Inside src/components/Button/Button.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> Button = <span class="hljs-function">(<span class="hljs-params">{ children, ...rest }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"button"</span> {<span class="hljs-attr">...rest</span>}&gt;</span>
      { children }
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>Here, we're creating a new component called Button that adds a class of <code>button</code> to the element and passes through the <code>children</code>. We're a additionally <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">destructuring</a> the rest of the props into the <code>rest</code> variable and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax">spreading</a> that value into the <code>&lt;button&gt;</code> element.</p>
<p>If you've followed along, your files should now look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/button-reaect-component.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Button component in React</em></p>
<h3 id="heading-using-our-new-button-component">Using our new Button component</h3>
<p>So now that we have our Button component, let's use it!</p>
<p>Open up the file <code>src/stories/1-Button.stories.js</code> and replace the line that's importing <code>Button</code> with:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/updating-button-storybook-story.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Updating the Button Storybook story</em></p>
<p>And once you hit save, you can open back up your browser tab with your Storybook dashboard, and you can now see a button that looks mostly similar, but it uses the browser's default styles for the <code>&lt;button&gt;</code> element. You'll even notice that if you click it, the event will still be logged under the Actions tab.</p>
<h3 id="heading-styling-our-button-component">Styling our Button component</h3>
<p>Finally, we probably don't want to use the browser default styles, so let's make it look nice.</p>
<p>In our <code>src/components/Button</code> directory, add a new file <code>Button.css</code> and add the following content:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Inside src/components/Button/Button.css */</span>

<span class="hljs-selector-class">.button</span> {
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">background-color</span>: blueviolet;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">padding</span>: .<span class="hljs-number">8em</span> <span class="hljs-number">1em</span>;
  <span class="hljs-attribute">border-radius</span>: .<span class="hljs-number">2rem</span>;
}
</code></pre>
<p>This applies a few styles to our <code>.button</code> class like adding a background color and changing the font color to white.</p>
<p>But if you open Storybook, you'll notice it didn't do anything. To use it, we need to import it into our component.</p>
<p>Inside <code>src/components/Button/Button.js</code> add the following at the top under the React import:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'./Button.css'</span>;
</code></pre>
<p>And once you save that and open up your browser, you should now see our new button with our updated styles!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/new-button-storybook.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Button in Storybook</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-storybook/commit/e71e0e9e666adee0455b0b69118053c2f551ab68">Follow along with the commit!</a></p>
<h2 id="heading-step-3-using-our-new-button-component">Step 3: Using our new Button component</h2>
<p>The ultimate goal of our component is to use it right? So let's add it to our app.</p>
<h3 id="heading-switching-over-to-the-react-app">Switching over to the React app</h3>
<p>First we'll need to either start our React app in a new terminal tab or kill the Storybook process and start the React process there. To start the React app using Create React App, run:</p>
<pre><code class="lang-shell">yarn start
# or
npm run start
</code></pre>
<p>Once that loads, we should have our standard Create React App if you're following along with me:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/new-create-react-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Create React App</em></p>
<h3 id="heading-importing-and-using-the-new-button">Importing and using the new button</h3>
<p>Next, inside of <code>src/App.js</code>, let's import our new Button at the top of the page:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Button'</span>;
</code></pre>
<p>With Button imported, we can use it. Here, we can simply add it anywhere we want in the page. I'm going to replace the Learn React link with:</p>
<pre><code class="lang-jsx">&lt;p&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span>&gt;</span>Hello, Storybook!<span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span></span>
&lt;/p&gt;
</code></pre>
<p>And if we save and reload the page, we should now see our Button on the page!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/create-react-app-with-new-button.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Button in Create React App</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-storybook/commit/e6071aae5be281101d486c4cc7664bf6cacb4028">Follow along with the commit</a></p>
<h2 id="heading-repeat-creating-a-new-header-component">Repeat: Creating a new Header component</h2>
<p>The great thing about Storybook and React (or any of the supported frameworks) is that this process scales to as many components as you want.</p>
<p>So let's build another component!</p>
<h3 id="heading-creating-our-header-component">Creating our Header component</h3>
<p>Similar to our Button, let's start off by creating the set of directories and files that give us our component.</p>
<p>Since we already did this once, I'm going to provide the code without walking through what's going on.</p>
<p>Let's start off by spinning back up our Storybook server with:</p>
<pre><code>yarn storybook
# or 
npm run storybook
</code></pre><p>Create a <code>Header</code> directory inside the <code>src/components</code> directory.</p>
<p>Create an <code>index.js</code> file inside of <code>src/components/Header</code> with the following content:</p>
<pre><code class="lang-js"><span class="hljs-comment">// In src/components/Header/index.js</span>

<span class="hljs-keyword">export</span> { <span class="hljs-keyword">default</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'./Header'</span>;
</code></pre>
<p>Create a <code>Header.js</code> file inside of <code>src/components/Header</code> with the following content:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// In src/components/Header/Header.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./Header.css'</span>;

<span class="hljs-keyword">const</span> Header = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"header"</span>&gt;</span>
      { children }
    <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header;
</code></pre>
<p>Create a <code>Header.css</code> file inside of <code>src/components/Header</code> with the following content:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* In src/components/Header/Header.css */</span>

<span class="hljs-selector-class">.header</span> {
  <span class="hljs-attribute">font-family</span>: sans-serif;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.5em</span>;
  <span class="hljs-attribute">color</span>: blueviolet;
  <span class="hljs-attribute">border-bottom</span>: solid <span class="hljs-number">5px</span> aqua;
  <span class="hljs-attribute">padding-bottom</span>: .<span class="hljs-number">2em</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">5px</span> <span class="hljs-number">0</span> blueviolet;
}
</code></pre>
<p>Now if you notice, if you try to open up Storybook, again, nothing will happen. This time we need to create a new story file.</p>
<h3 id="heading-creating-a-new-story-file">Creating a new Story file</h3>
<p>Inside <code>src/stories</code>, add a new file called <code>2-Header.stories.js</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Inside src/stories/2-Header.stories.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">import</span> Header <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Header'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">title</span>: <span class="hljs-string">'Header'</span>,
  <span class="hljs-attr">component</span>: Header,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Text = <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Header</span>&gt;</span>Hello Header<span class="hljs-tag">&lt;/<span class="hljs-name">Header</span>&gt;</span></span>;
</code></pre>
<p>Here's a breakdown of our story file:</p>
<ul>
<li>First, we import our component – this is pretty standard any time we want to use it</li>
<li>The first thing we export is a <code>default</code> object. With Storybook, it expects the default export to be the configuration of our story, so here we provide it with a title and we pass in the component that we're using for this story</li>
<li>The second and last thing we export is the <code>Text</code> constant. With Storybook, any non-default export will be considered a variation that will get nested under the title that you provide in the default export</li>
</ul>
<p>And if you save this file and open up your Storybook dashboard in the browser, you should now see the new header!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/new-header-storybook-story.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Header component in Storybook</em></p>
<h3 id="heading-using-the-header-component">Using the Header component</h3>
<p>Using our component is just the same as our Button component, so inside of <code>src/App.js</code>, let's add our Header.</p>
<p>After starting your React server, first import our new Header:</p>
<pre><code class="lang-js"><span class="hljs-comment">// In src/App.js</span>

<span class="hljs-keyword">import</span> Header <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Header'</span>;
</code></pre>
<p>Then add it to the top of the page:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// In src/App.js</span>

&lt;Header&gt;My App&lt;/Header&gt;
</code></pre>
<p>And if you open the page, we'll see our new Header!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/create-react-app-with-header-and-button.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Create React App with new Header and Button components</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-storybook/commit/e1c59eccaf5f4146a2fe039dca8874609d615194">Follow along with the commit!</a></p>
<h2 id="heading-adding-more-components">Adding more components</h2>
<p>As you've noticed with our second Repeat step – adding a new component is pretty much the same process for any type of component we want to add. Once we have it in our library, we can develop it in a focused environment and then import it to our app to use.</p>
<p>You can now use this to manage your library of components and better maintain an entire system for your project!</p>
<h2 id="heading-more-storybook-features">More Storybook features</h2>
<p>Storybook doesn't stop with just adding components, it provides the ability to configure <a target="_blank" href="https://storybook.js.org/addons/">Addons</a> that enhance the core capabilities opening up a lot of possibilities.</p>
<p>Here are some of my favorites...</p>
<h3 id="heading-story-source">Story Source</h3>
<p>When building a component system, the hope is that people can easily use these components. But if you don't have documentation, someone would have to open up the file or try to find another use example.</p>
<p>Instead, <a target="_blank" href="https://github.com/storybookjs/storybook/tree/master/addons/storysource">Story Source</a> shows the code source of the story file you created allowing someone browsing your Storybook dashboard to get an example right along with the component output!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/storybook-source-demo.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Storybook Story Source demo</em></p>
<h3 id="heading-storyshots">Storyshots</h3>
<p>If you're a fan of automated testing, you might have heard of using <a target="_blank" href="https://jestjs.io/">Jest</a> or another tool for adding snapshot testing to your app.</p>
<p><a target="_blank" href="https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core">StoryShots</a> is a way to easily add Jest snapshot testing to your component system. It creates snapshots based off of the stories you create so you can make sure that your components aren't fundamentally changing (or breaking) during development.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/storybook-snapshot-example.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Snapshot example with StoryShots</em></p>
<h2 id="heading-whats-your-favorite-part-of-storybook">What's your favorite part of Storybook?</h2>
<p><a target="_blank" href="https://twitter.com/colbyfayock">Share with me on Twitter!</a></p>
<h2 id="heading-continue-the-conversation">Continue the conversation!</h2>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1270392710260719616"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Tailwind CSS and How Can I Add it to my Website or React App? ]]>
                </title>
                <description>
                    <![CDATA[ CSS is a technology that can be your best or worst friend. While it's incredibly flexible and can produce what seems like magic, without the proper care and attention, it can become hard to manage like any other code.  How can Tailwind CSS help us to... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-tailwind-css-and-how-can-i-add-it-to-my-website-or-react-app/</link>
                <guid isPermaLink="false">66b8e39e0196ee8e3efce546</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS Framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ front end ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PostCSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 19 May 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/05/tailwind-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>CSS is a technology that can be your best or worst friend. While it's incredibly flexible and can produce what seems like magic, without the proper care and attention, it can become hard to manage like any other code. </p>
<p>How can Tailwind CSS help us to take control of our styles?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-tailwind">What is Tailwind?</a></li>
<li><a class="post-section-overview" href="#heading-so-what-makes-tailwind-great">So what makes Tailwind great?</a></li>
<li><a class="post-section-overview" href="#heading-part-1-adding-tailwind-css-to-a-static-html-page">Part 1: Adding Tailwind CSS to a static HTML page</a></li>
<li><a class="post-section-overview" href="#heading-part-2-adding-tailwind-css-to-a-react-app">Part 2: Adding Tailwind CSS to a React app</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/7KeZcRMltP0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-tailwind">What is Tailwind?</h2>
<p><a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a> is a "utility-first" CSS framework that provides a deep catalog of CSS classes and tools that lets you easily get started styling your website or application.</p>
<p>The underlying goal is that as you're building your project, you don't need to deal with cascading styles and worrying about how to override that 10-selector pileup that's been haunting your app for the last 2 years.</p>
<h2 id="heading-so-what-makes-tailwind-great">So what makes Tailwind great?</h2>
<p>Taildwind's solution is to provide a wide variety of CSS classes that each have their own focused use. Instead of a class called <code>.btn</code> that is created with a bunch of CSS attributes directly, in Tailwind, you would either apply a bunch of classes like <code>bg-blue-500 py-2 px-4 rounded</code> to the button element or build a <code>.btn</code> class by <a target="_blank" href="https://tailwindcss.com/docs/functions-and-directives/#apply">applying</a> those utility class to that selector.</p>
<p>While Tailwind has a lot more going for it, we're going to focus on these basics for this tutorial, so let's dig in!</p>
<h2 id="heading-part-1-adding-tailwind-css-to-a-static-html-page">Part 1: Adding Tailwind CSS to a static HTML page</h2>
<p>We're going to start off by applying Tailwind CSS straight to a static HTML page. The hope is that by focusing on Tailwind and not the app, we can get a better understanding of what's actually happening with the framework.</p>
<h3 id="heading-step-1-creating-a-new-page">Step 1: Creating a new page</h3>
<p>You can get started by simply creating a new HTML file. For the content, you can use whatever you want, but I'm going to use <a target="_blank" href="http://fillerama.io/">fillerama.io</a> so the filler content is a bit more fun.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-with-content.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New HTML page with content</em></p>
<p>If you want to simplify this step, you can just <a target="_blank" href="https://github.com/colbyfayock/my-tailwind-static/commit/c7db11899c9cd193cdd666fd228cfaefe75623f2#diff-eacf331f0ffc35d4b482f1d15a887d3b">copy the file I created</a> to get started.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-tailwind-static/commit/c7db11899c9cd193cdd666fd228cfaefe75623f2">Follow along with the commit!</a></p>
<h3 id="heading-step-2-adding-tailwind-css-via-cdn">Step 2: Adding Tailwind CSS via CDN</h3>
<p>Tailwind typically recommends that you install through <a target="_blank" href="https://www.npmjs.com/package/tailwindcss">npm</a> to get the full functionality, but again, we're just trying to understand how this works first.</p>
<p>So let's add a link to the CDN file in the <code>&lt;head&gt;</code> of our document:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
</code></pre>
<p>Once you save and reload the page, the first thing you'll notice is that all of the styles were stripped!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-tailwind-base.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with the Tailwind CSS base</em></p>
<p>This is expected. Tailwind includes a set of <a target="_blank" href="https://tailwindcss.com/docs/preflight">preflight styles</a> to fix cross-browser inconsistencies. For one, they include the popular <a target="_blank" href="https://github.com/necolas/normalize.css/">normalize.css</a> which they build upon with their own styles.</p>
<p>But we're going to learn how to use Tailwind to add back our styles and set things up how we want!</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-tailwind-static/commit/b431b75cee0a03154a70b194b6dfcf028bc65942">Follow along with the commit!</a></p>
<h3 id="heading-step-3-using-tailwind-css-to-add-styles-to-your-page">Step 3: Using Tailwind CSS to add styles to your page</h3>
<p>Now that we have Tailwind installed, we've added the ability to make use of their huge library of utility classes that we'll now use to add styles back to our page.</p>
<p>Let's start off by adding some margin to all of our paragraphs (<code>&lt;p&gt;</code>) and our list elements (<code>&lt;ol&gt;</code>, <code>&lt;ul&gt;</code>). We can do this by adding the <code>.my-5</code> class to each element like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"my-5"</span>&gt;</span>
  Bender, quit destroying the universe! Yeah, I do that with my stupidness. I never loved you. Moving along…
  Belligerent and numerous.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>The class name follows a pattern that you'll notice with the rest of the utility classes – <code>.my-5</code> stands for margin (m) applied to the y-axis (y) with a value of 5 which in Tailwind's case, it uses <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units">rem</a>, so the value is 5rem.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-paragraph-styles.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with basic paragraph styles</em></p>
<p>Next, let's make our headers look like actual headers. Starting with our <code>h1</code> tag, let's add:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold mt-8 mb-5"</span>&gt;</span>
</code></pre>
<p>Here's what's happening:</p>
<ul>
<li><code>text-2xl</code>: set the text size (font-size) to 2xl. In Tailwind, that 2xl will equate to 1.5rem</li>
<li><code>font-bold</code>: set the weight of the text (font-weight) to bold</li>
<li><code>mt-8</code>: Similar to <code>my-5</code>, this will set the margin top (t) to 8rem</li>
<li><code>mb-5</code>: And this will set the margin bottom (b) to 5rem</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-header-styles.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with styled H1</em></p>
<p>With those classes added to the <code>h1</code>, let's apply those same exact classes to the rest of our header elements, but as we go down the list, reduce the size of the font size, so it will look like:</p>
<ul>
<li>h2: <code>text-xl</code></li>
<li>h3: <code>text-lg</code></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-all-headers-style.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with all headers styled</em></p>
<p>Now let's make our list elements look like lists. Starting with our unordered list (<code>&lt;ul&gt;</code>), let's add these classes:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"list-disc list-inside my-5 pl-2"</span>&gt;</span>
</code></pre>
<p>Here's what we're adding:</p>
<ul>
<li><code>list-disc</code>: set the list-style-stype to disc (the markers on each line item)</li>
<li><code>list-inside</code>: sets the position of the list markers using  relative to the list items and the list itself with list-style-position to inside</li>
<li><code>my-5</code>: set the margin of the y axis to 5rem</li>
<li><code>pl-2</code>: set the left padding to 2rem</li>
</ul>
<p>Then we can apply the exact same classes to our ordered list (<code>&lt;ol&gt;</code>), except instead of <code>list-disc</code>, we want to change our style type to <code>list-decimal</code>, so that we can see numbers given it's an ordered list.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ol</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"list-decimal list-inside my-5 pl-2"</span>&gt;</span>
</code></pre>
<p>And we have our lists!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-styled-lists.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with styled lists</em></p>
<p>Finally, let's make our content a little easier to read by setting a max width and centering the content. On the <code>&lt;body&gt;</code> tag, add the following:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-4xl mx-auto"</span>&gt;</span>
</code></pre>
<p>/Note: Typically you wouldn't want to apply these classes to the <code>&lt;body&gt;</code> itself, rather, you can wrap all of your content with a <code>&lt;main&gt;</code> tag, but since we're just trying to get an idea of how this works, we'll roll with this. Feel free to add the <code>&lt;main&gt;</code> tag with those classes instead if you prefer!/</p>
<p>And with that, we have our page!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-content-centered.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with centered content</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-tailwind-static/commit/06fd719c98d17e2242b61ec2ab7034436c1c2ba6">Follow along with the commit!</a></p>
<h3 id="heading-step-4-adding-a-button-and-other-components">Step 4: Adding a button and other components</h3>
<p>For the last part of our static example, let's add a button.</p>
<p>The trick with Tailwind, is they intentionally don't provide pre-baked component classes with the idea being that likely people would need to override these components anyways to make them look how they wanted.</p>
<p>So that means, we're going to have to create our own using the utility classes!</p>
<p>First, let's add a new button. Somewhere on the page, add the following snippet. I'm going to add it right below the first paragraph (<code>&lt;p&gt;</code>) tag:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Party with Slurm!<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-unstyled-button.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with unstyled button</em></p>
<p>You'll notice just like all of the other elements, that it's unstyled, however, if you try clicking it, you'll notice it still has the click actions. So let's make it look like a button.</p>
<p>Let's add the following classes:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-white font-bold bg-purple-700 hover:bg-purple-800 py-2 px-4 rounded"</span>&gt;</span>
  Party with Slurm!
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Here's a breakdown of what's happening:</p>
<ul>
<li><code>text-white</code>: we're setting our text color to white</li>
<li><code>font-bold</code>: set the weight of the text to bold (font-weight)</li>
<li><code>bg-purple-700</code>: set the background color of the button to purple with a shade of 700. The 700 is relative to the other colors defined as purple, you can find these values on their <a target="_blank" href="https://tailwindcss.com/docs/customizing-colors#default-color-palette">palette documentation page</a></li>
<li><code>hover:bg-purple-800</code>: when someone hovers over the button, set the background color to purple shade 800. Tailwind provides these helper classes that allow us to easily define interactive stiles with things like <a target="_blank" href="https://tailwindcss.com/course/hover-focus-and-active-styles/">hover, focus, and active modifiers</a></li>
<li><code>py-2</code>: set the padding of the y-axis to 2rem</li>
<li><code>px-4</code>: set the padding of the  x-axis to 4rem</li>
<li><code>rounded</code>: round the corners of the element by setting the border radius. With tailwind, it sets the border-radius value to .25rem</li>
</ul>
<p>And with all of that, we have our button!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/html-page-styled-button.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>HTML page with a styled button</em></p>
<p>You can apply this methodology to any other component that you'd like to build. Though it's a manual process, we'll find out how we can make this process easier when building in more dynamic projects like those based on React.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-tailwind-static/commit/09312336dce316a75e8007d6c935133490f16c25">Follow along with the commit!</a></p>
<h2 id="heading-part-2-adding-tailwind-css-to-a-react-app">Part 2: Adding Tailwind CSS to a React app</h2>
<p>For more of a real-world use case, we're going to add Tailwind CSS to an app created with <a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">Create React App</a>.</p>
<p>First, we'll walk through the steps you need to take to add tailwind to your project using a fresh install of Create React App, then we'll use our content from our last example to recreate it in React.</p>
<h3 id="heading-step-1-spinning-up-a-new-react-app">Step 1: Spinning up a new React app</h3>
<p>I'm not going to detail this step out too much. The gist is we'll bootstrap a new React app using Create React App.</p>
<p>To get started, you can follow along <a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">with the directions</a> from the official React documentation:</p>
<p><a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">https://reactjs.org/docs/create-a-new-react-app.html</a></p>
<p>And once you start your development server, you should now see an app!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/create-react-app-starting-page.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Create React App starting page</em></p>
<p>Finally, let's migrate all of our old content to our app. To do this, copy everything inside of the <code>&lt;body&gt;</code> tag of our static example and paste it inside of the wrapper <code>&lt;div className="App"&gt;</code> in the new React project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/code-migrating-content.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Migrating code to React app</em></p>
<p>Next, change all <code>class="</code> attributes from the content we pasted in to <code>className="</code> so that it's using proper React attributes:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/code-fixing-class-attribute.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Fixing class attribute in content</em></p>
<p>And lastly, replace the className <code>App</code> on our wrapper <code>&lt;div&gt;</code> to the classes we used on our static <code>&lt;body&gt;</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/code-wrapper-styles.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding wrapper styles to the app</em></p>
<p>Once you save your changes and spin back up your server, it will look deceivingly okay.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/react-app-basic-content.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>React app with basic content</em></p>
<p>React includes some basic styles itself, so while it looks okay, we're not actually using Tailwind yet. So let's get started by installing it!</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-tailwind-dynamic/commit/57993883c77739f71072bcc02ed2398543efc2fd">Follow along with the commit!</a></p>
<h3 id="heading-step-2-installing-tailwind-in-your-react-app">Step 2: Installing Tailwind in your React app</h3>
<p>There are a few steps we'll need to go through in order to get Tailwind up and running on our app. Make sure you follow these steps carefully to ensure it's properly configured.</p>
<p>First, let's add our dependencies:</p>
<pre><code>yarn add tailwindcss postcss-cli autoprefixer
# or
npm install tailwindcss postcss-cli autoprefixer
</code></pre><p><a target="_blank" href="https://tailwindcss.com/docs/installation#4-process-your-css-with-tailwind">Per Tailwind's documentation</a>, we need to be able to process our styles so that they can be properly added to our pipeline. So in the above, we're adding:</p>
<ul>
<li><a target="_blank" href="https://tailwindcss.com/">tailwindcss</a>: the core Tailwind package</li>
<li><a target="_blank" href="https://github.com/postcss/postcss">postcss-cli</a>: Create React App already uses postcss, but we need to configure Tailwind to be part of that build process and run it's own processing</li>
<li><a target="_blank" href="https://github.com/postcss/autoprefixer">autoprefixer</a>: Tailwind doesn't include vendor prefixes, so we want to add autoprefixer to handle this for us. This runs as part of our postcss configuration</li>
</ul>
<p>We're also going to add two dev dependencies that make it easier to work with our code:</p>
<pre><code>yarn add concurrently chokidar-cli -D
# or
npm install concurrently chokidar-cli --save-dev
</code></pre><ul>
<li><a target="_blank" href="https://github.com/kimmobrunfeldt/concurrently">concurrently</a>: a package that lets us set up the ability to run multiple commands at once. This is helpful since we'll need to watch both the styles and React app itself.</li>
<li><a target="_blank" href="https://github.com/kimmobrunfeldt/chokidar-cli">chokidar-cli</a>: let's us watch files and run a command when changed. We'll use this to watch our CSS file and run the build process of the CSS on cahnge</li>
</ul>
<p>Next, let's configure postcss, so create a new file in the root of your project called <code>postcss.config.js</code> and include the following:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Inside postcss.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
    <span class="hljs-attr">plugins</span>: [
        <span class="hljs-built_in">require</span>(<span class="hljs-string">'tailwindcss'</span>),
        <span class="hljs-built_in">require</span>(<span class="hljs-string">'autoprefixer'</span>)
    ],
};
</code></pre>
<p>This will add the Tailwindcss and Autoprefixer plugins to our postcss config.</p>
<p>With our configuration, we need to include it as part of the build and watch processes. Inside <code>package.json</code>, add the following to definitions to your <code>scripts</code> property:</p>
<pre><code class="lang-json"><span class="hljs-string">"build:css"</span>: <span class="hljs-string">"tailwind build src/App.css -o src/index.css"</span>,
<span class="hljs-string">"watch:css"</span>: <span class="hljs-string">"chokidar 'src/App.css' -c 'npm run build:css'"</span>,
</code></pre>
<p>Additionally, modify the <code>start</code> and <code>build</code> scripts to now include those commands:</p>
<pre><code class="lang-json"><span class="hljs-string">"start"</span>: <span class="hljs-string">"concurrently -n Tailwind,React 'npm run watch:css' 'react-scripts start'"</span>,
<span class="hljs-string">"build"</span>: <span class="hljs-string">"npm run build:css &amp;&amp; react-scripts build"</span>,
</code></pre>
<p>With our configuration ready to go, let's try our styles back to where they were when we left off from the static example.</p>
<p>Inside the <code>App.css</code> file, replace the entire content with:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<p>This is going to import Tailwind's base styles, components, and utility classes that allow Tailwind to work as you would expect it to.</p>
<p>We can also remove the <code>App.css</code> import from our <code>App.js</code> file because it's now getting injected directly into our <code>index.css</code> file. So remove this line:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>;
</code></pre>
<p>Once you're done, you can start back up your development server! If it was already started, make sure to restart it so all of the configuration changes take effect.</p>
<p>And now the page should look exactly like it did in our static example!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/react-app-with-styled-content.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>React app with content styled</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-tailwind-dynamic/commit/5f50cc218ef58f469dad7f09bdad31f36b58a896">Follow along with the commit!</a></p>
<h3 id="heading-step-3-creating-a-new-button-component-class-with-tailwind">Step 3: Creating a new button component class with Tailwind</h3>
<p>Tailwind doesn't ship with prebaked component classes, but it does make it easy to create them!</p>
<p>We're going to use our button that we already created as an example of creating a new component. We'll create a new class <code>btn</code> as well as a color modifier <code>btn-purple</code> to accomplish this.</p>
<p>The first thing we'll want to do is open up our App.css file where we'll create our new class. Inside that file, let's add:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn</span> {
  @apply font-bold py-2 px-4 rounded;
}
</code></pre>
<p>If you remember from our HTML, we're already including those same classes to our <code>&lt;button&gt;</code> element.  Tailwind let's us "apply" or include the styles that make up these utility classes to another class, in this case, the <code>.btn</code> class.</p>
<p>And now that we're creating that class, let's apply it to our button:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"btn text-white bg-purple-700 hover:bg-purple-800"</span>&gt;</span>
  Party with Slurm!
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>And if we open up our page, we can see our button still looks the same. If we inspect the element, we can see our new <code>.btn</code> class generated with those styles.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/react-tailwind-button-class.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>.btn class in a React app with Tailwind</em></p>
<p>Next, let's create a color modifier. Similar to what we just did, we're going to add the following rules:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.btn-purple</span> {
  @apply bg-purple-700 text-white;
}

<span class="hljs-selector-class">.btn-purple</span><span class="hljs-selector-pseudo">:hover</span> {
  @apply bg-purple-800;
}
</code></pre>
<p>Here, we're adding our background color and our text color to our button class. We're also applying a darker button color when someone hovers over the button.</p>
<p>We'll also want to update our HTML button to include our new class and remove the ones we just applied:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"btn btn-purple"</span>&gt;</span>
  Party with Slurm!
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>And with that change, we can still see that nothing has changed and we have our new button class!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/react-tailwind-styled-button.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Styled button in React with Tailwind</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-tailwind-dynamic/commit/7a76e8a4583b0a4c523ea902d73e889c7b86f437">Follow along with the commit!</a></p>
<h2 id="heading-applying-these-concepts-to-more-components">Applying these concepts to more components</h2>
<p>Through this walkthrough, we learned how to create a new component class using the Tailwind apply directive. This allowed us to create reusable classes that represent a component like a button.</p>
<p>We can apply this to any number of components in our design system. For instance, if we wanted to always show our lists the way we set them up here, we could create a <code>.list-ul</code> class that represented an unordered list with the Tailwind utilities <code>list-disc list-inside my-5 pl-2</code> applied.</p>
<h2 id="heading-what-tips-and-tricks-do-you-like-to-use-with-tailwind">What tips and tricks do you like to use with Tailwind?</h2>
<p>Share with me on <a target="_blank" href="https://twitter.com/colbyfayock">Twitter</a>!</p>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to set up a lightweight, tool-agnostic CI/CD flow with GitHub Actions ]]>
                </title>
                <description>
                    <![CDATA[ Agnostic tooling is the clever notion that you should be able to run your code in various environments. With many continuous integration and continuous development (CI/CD) apps available, agnostic tooling gives developers a big advantage: portability... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-lightweight-tool-agnostic-ci-cd-flow-with-github-actions/</link>
                <guid isPermaLink="false">66bd8f04de1f3ee5a6a8f4c5</guid>
                
                    <category>
                        <![CDATA[ continuous delivery ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Continuous Integration ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Victoria Drake ]]>
                </dc:creator>
                <pubDate>Tue, 29 Oct 2019 12:13:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/10/cover-3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Agnostic tooling is the clever notion that you should be able to run your code in various environments. With many continuous integration and continuous development (CI/CD) apps available, agnostic tooling gives developers a big advantage: portability.</p>
<p>Of course, having your CI/CD work <em>everywhere</em> is a tall order. Popular <a target="_blank" href="https://github.com/marketplace/category/continuous-integration">CI apps for GitHub repositories</a> alone use a multitude of configuration languages spanning <a target="_blank" href="https://groovy-lang.org/syntax.html">Groovy</a>, <a target="_blank" href="https://yaml.org/">YAML</a>, <a target="_blank" href="https://github.com/toml-lang/toml">TOML</a>, <a target="_blank" href="https://json.org/">JSON</a>, and more… all with differing syntax, of course. Porting workflows from one tool to another is more than a one-cup-of-coffee process.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/cover-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The introduction of <a target="_blank" href="https://github.com/features/actions">GitHub Actions</a> has the potential to add yet another tool to the mix; or, for the right set up, greatly simplify a CI/CD workflow.</p>
<p>Prior to this article, I accomplished my CD flow with several lashed-together apps. I used AWS Lambda to trigger site builds on a schedule. I had Netlify build on push triggers, as well as run image optimization, and then push my site to the public Pages repository. I used Travis CI in the public repository to test the HTML. All this  worked in conjunction with GitHub Pages, which actually hosts the site.</p>
<p>I’m now using the GitHub Actions beta to accomplish all the same tasks, with one <a target="_blank" href="https://victoria.dev/blog/a-portable-makefile-for-continuous-delivery-with-hugo-and-github-pages/">portable Makefile</a> of build instructions, and without any other CI/CD apps.</p>
<h2 id="heading-appreciating-the-shell">Appreciating the shell</h2>
<p>What do most CI/CD tools have in common? They run your workflow  instructions in a shell environment! This is wonderful, because that means that most CI/CD tools can do anything that you can do in a  terminal… and you can do pretty much <em>anything</em> in a terminal.</p>
<p>Especially for a contained use case like building my static site with a generator like Hugo, running it all in a shell is a no-brainer. To  tell the magic box what to do, we just need to write instructions.</p>
<p>While a shell script is certainly the most portable option, I use the still-very-portable <a target="_blank" href="https://en.wikipedia.org/wiki/Make_(software)">Make</a> to write my process instructions. This provides me with some advantages over simple shell scripting, like the use of variables and <a target="_blank" href="https://en.wikipedia.org/wiki/Make_(software)#Macros">macros</a>, and the modularity of <a target="_blank" href="https://en.wikipedia.org/wiki/Makefile#Rules">rules</a>.</p>
<p>I got into the <a target="_blank" href="https://victoria.dev/blog/a-portable-makefile-for-continuous-delivery-with-hugo-and-github-pages/">nitty-gritty of my Makefile in my last post</a>. Let’s look at how to get GitHub Actions to run it.</p>
<h2 id="heading-using-a-makefile-with-github-actions">Using a Makefile with GitHub Actions</h2>
<p>To our point on portability, my magic Makefile is stored right in the  repository root. Since it’s included with the code, I can run the Makefile locally on any system where I can clone the repository, provided I set the environment variables. Using GitHub Actions as my CI/CD tool is as straightforward as making Make go worky-worky.</p>
<p>I found the <a target="_blank" href="https://help.github.com/en/articles/workflow-syntax-for-github-actions">GitHub Actions workflow syntax guide</a> to be pretty straightforward, though also lengthy on options. Here’s the necessary set up for getting the Makefile to run.</p>
<p>The workflow file at <code>.github/workflows/make-master.yml</code> contains the following:</p>
<pre><code>name: make-master

<span class="hljs-attr">on</span>:
  push:
    branches:
      - master
  <span class="hljs-attr">schedule</span>:
    - cron: <span class="hljs-string">'20 13 * * *'</span>

<span class="hljs-attr">jobs</span>:
  build:
    runs-on: ubuntu-latest
    <span class="hljs-attr">steps</span>:
      - uses: actions/checkout@master
        <span class="hljs-attr">with</span>:
          fetch-depth: <span class="hljs-number">1</span>
      - name: Run Makefile
        <span class="hljs-attr">env</span>:
          TOKEN: ${{ secrets.TOKEN }}
        <span class="hljs-attr">run</span>: make all
</code></pre><p>I’ll explain the components that make this work.</p>
<h2 id="heading-triggering-the-workflow">Triggering the workflow</h2>
<p>Actions support multiple <a target="_blank" href="https://help.github.com/en/articles/events-that-trigger-workflows">triggers for a workflow</a>. Using the <code>on</code> syntax, I’ve defined two triggers for mine: a <a target="_blank" href="https://help.github.com/en/articles/workflow-syntax-for-github-actions#onpushpull_requestbranchestags">push event</a> to the <code>master</code> branch only, and a <a target="_blank" href="https://help.github.com/en/articles/events-that-trigger-workflows#scheduled-events-schedule">scheduled</a> <code>cron</code> job.</p>
<p>Once the <code>make-master.yml</code> file is in your repository, either of your triggers will cause Actions to run your Makefile. To see how  the last run went, you can also <a target="_blank" href="https://help.github.com/en/articles/configuring-a-workflow#adding-a-workflow-status-badge-to-your-repository">add a fun badge</a> to the README.</p>
<h3 id="heading-one-hacky-thing">One hacky thing</h3>
<p>Because the Makefile runs on every push to <code>master</code>, I sometimes would get errors when the site build had no changes. When Git, via <a target="_blank" href="https://victoria.dev/blog/a-portable-makefile-for-continuous-delivery-with-hugo-and-github-pages/">my Makefile</a>, attempted to commit to the Pages repository, no changes were detected and the commit would fail annoyingly:  </p>
<pre><code>nothing to commit, working tree clean
On branch master
Your branch is up to date <span class="hljs-keyword">with</span> <span class="hljs-string">'origin/master'</span>.
nothing to commit, working tree clean
<span class="hljs-attr">Makefile</span>:<span class="hljs-number">62</span>: recipe <span class="hljs-keyword">for</span> target <span class="hljs-string">'deploy'</span> failed
<span class="hljs-attr">make</span>: *** [deploy] <span class="hljs-built_in">Error</span> <span class="hljs-number">1</span>
##[error]Process completed <span class="hljs-keyword">with</span> exit code <span class="hljs-number">2.</span>
</code></pre><p>I came across some solutions that proposed using <code>diff</code> to check if a commit should be made, but this may not work for <a target="_blank" href="https://github.com/benmatselby/hugo-deploy-gh-pages/issues/4">reasons</a>. As a workaround, I simply added the <a target="_blank" href="https://gohugo.io/functions/format/#use-local-and-utc">current UTC time</a> to my index page so that every build would contain a change to be committed.</p>
<h2 id="heading-environment-and-variables">Environment and variables</h2>
<p>You can define the <a target="_blank" href="https://help.github.com/en/github/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources">virtual environment</a> for your workflow to run in using the <code>runs-on</code> syntax. The obvious best choice one I chose is Ubuntu. Using <code>ubuntu-latest</code> gets me the most updated version, whatever that happens to be when you're reading this.</p>
<p>GitHub sets some <a target="_blank" href="https://help.github.com/en/github/automating-your-workflow-with-github-actions/using-environment-variables#default-environment-variables">default environment variables</a> for workflows. The <a target="_blank" href="https://github.com/actions/checkout"><code>actions/checkout</code> action</a> with <code>fetch-depth: 1</code> creates a copy of just the most recent commit your repository in the <code>GITHUB_WORKSPACE</code> variable. This allows the workflow to access the Makefile at <code>GITHUB_WORKSPACE/Makefile</code>. Without using the checkout action, the Makefile won't be found, and I get an error that looks like this:  </p>
<pre><code>make: *** No rule to make target <span class="hljs-string">'all'</span>.  Stop.
Running Makefile
##[error]Process completed <span class="hljs-keyword">with</span> exit code <span class="hljs-number">2.</span>
</code></pre><p>While there is a <a target="_blank" href="https://help.github.com/en/github/automating-your-workflow-with-github-actions/authenticating-with-the-github_token">default <code>GITHUB_TOKEN</code> secret</a>,  this is not the one I used. The default is only locally scoped to the  current repository. To be able to push to my separate GitHub Pages  repository, I created a <a target="_blank" href="https://github.com/settings/tokens">personal access token</a> scoped to <code>public_repo</code> and pass it in as the <code>secrets.TOKEN</code> encrypted variable. For a step-by-step, see <a target="_blank" href="https://help.github.com/en/github/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets">Creating and using encrypted secrets</a>.</p>
<h2 id="heading-portable-tooling">Portable tooling</h2>
<p>The nice thing about using a simple Makefile to define the bulk of my  CI/CD process is that it’s completely portable. I can run a Makefile  anywhere I have access to an environment, which is most CI/CD apps,  virtual instances, and, of course, on my local machine.</p>
<p>One of the reasons I like GitHub Actions is that getting my Makefile  to run was pretty straightforward. I think the syntax is well done - easy to read, and intuitive when it comes to finding an option you’re  looking for. For someone already using GitHub Pages, Actions provides a  pretty seamless CD experience; and if that should ever change, I can run my Makefile elsewhere. ¯_(ツ)_/¯</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Assisted script writing, with Pychronia Tools ]]>
                </title>
                <description>
                    <![CDATA[ By Pakal de Bonchamp Let software tooling check the consistency of your roleplay scripts for you! (French version of this article available here on Electro-GN) Issue Every  writer will confirm it to you: it is not easy to remain coherent when  you ar... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/assisted-script-writing-with-pychronia-tools/</link>
                <guid isPermaLink="false">66d4608d9f2bec37e2da0660</guid>
                
                    <category>
                        <![CDATA[ larp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ roleplay ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Script ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ writing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 05 Jul 2019 21:44:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/pychroniatools.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Pakal de Bonchamp</p>
<p><strong>Let software tooling check the consistency of your roleplay scripts for you!</strong></p>
<p><em>(French version of this article available</em> <a target="_blank" href="https://www.electro-gn.com/12495-lecriture-de-scenarios-assistee-avec-pychronia-tools"><em>here on Electro-GN</em></a><em>)</em></p>
<h3 id="heading-issue">Issue</h3>
<p>Every  writer will confirm it to you: it is not easy to remain coherent when  you are working, for several months, on a long story. It is even less so  when the story in question is lived by dozens of characters, each with  their own partial vision of the truth. And live action roleplay games  (LARPs) are especially exposed to this problem.</p>
<p>The  danger does not lie in the first draft of the writing: if he has all  his ideas well in place, the author does not risk much, except for a few  typos and interchanges of names. It is during subsequent modifications  (changes in the chronology of facts, additions of twists and turns,  etc.) that the information — disseminated and duplicated in the  documents of the various participants — will gradually become obsolete  and inconsistent. Eventually, even the character sheets of a common  “murder&amp;mystery party” will end up full of spatial, temporal,  lexical and structural improbabilities if they are not rigorously  checked and compared after each evolution of the scenario.</p>
<p>What can we do to prevent this, without spending a lifetime doing comparative proofreading? First, undoubtedly, <em>deduplicate texts common to several players</em>, which lead to tedious copy and paste (multiplying errors and artificially inflating the mass of the text). Then, <em>allow the author to review the interdependent sets of changes</em> he has successively applied to his scenario. Finally, give him <em>summaries of key information</em>, easier to review than verbose literary texts. And all this in <em>as automated a way as possible</em>,  because the number of documents to be managed can make the smallest  operation very time-consuming (and at risk of carelessness).</p>
<h3 id="heading-the-benefits-of-pychronia-tools-machinery">The benefits of Pychronia Tools machinery</h3>
<p>To benefit from these precious writing facilities during the creation of the <a target="_blank" href="https://chrysalis-game.com/fr/cms/soiree-mystere-chrysalis-mindstorm/">Chrysalis:Mindstorm</a> mystery party (French only), I set up a specific writing process,  involving simple text files (which contain the scenario), various  existing software tools, as well as a module “Pychronia Tools” developed  for the occasion.</p>
<p>Once  this machinery is in place, all you have to do is start it up  and — magic — a hundred PDF files appear one after the other in the  output folder: game master’s manual, complete and summarized character  sheets for players, documents to print on beautiful scrolls to serve as  clues in play, summary sheets for extras….</p>
<p>This tool is much more than a simple document generator: it includes an <strong>automatic check of the script consistency</strong>.  Now, if an index is mentioned in one place but not provided in another,  or if the same symbol has different values from one file to another,  the error is reported.  </p>
<p><strong>Extract of consistency errors returned by the program:</strong></p>
<blockquote>
<p>!!! ERROR IN hints registry for key golden_box_with_blood : [‘needed’] requires a provided hint<br>!!! ERROR IN symbols registry for key murder_date : [‘3 January 2018′,’15 March 2018']</p>
</blockquote>
<p>And  thanks to the automatically generated summary tables, it is possible to  check at a glance that each player is well informed of the facts  concerning him/her, that the distribution of key information is more or  less balanced, and that major events are correctly recorded in summary  sheets.</p>
<p><strong>Extract from the automatic summary of the “facts” of the scenario:</strong></p>
<p><em>Character  names are in italics when they have the “fact” in question only in  their complete character sheet, not in their summary sheet.</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ytuGvYFYAwYxk2h9mf-Z_A.png" alt="Image" width="800" height="343" loading="lazy"></p>
<p>As  a result, the game master can rely quietly on his own (self-generated)  documents to set up and monitor the game: detailed scenario, evening  planning, checklist of materials and writings to be placed in the  premises, automatic summary of missions and special skills of each  player…</p>
<p>On  top of that, this machine can also send its game documents, by email,  to each player (e.g. character card and documents initially owned). This  avoids the drama that awaits each organizer: spoiling a participant by  sending him the wrong documents.</p>
<p>This  system obviously adds a certain complexity to the project, compared to  some common Word/LibreOffice files. But it provides invaluable support  in terms of scenario scalability and robustness, detecting  inconsistencies and automating daunting tasks. Personally, he saved me  more than once, when I switched the names of some characters in cards,  or forgot to warn some players of the new misdeeds they were supposed to  have committed in the past.</p>
<h3 id="heading-how-to-set-up-such-a-process">How to set up such a process?</h3>
<p><strong>Step 1:</strong> Move away from rich office files (docx, odt, pdf…) to a plain text  format, easily manipulable, where the formatting is explicitly indicated  by special characters. The documents <em>in game</em> with  high graphic and typographical needs (posters, scrolls, newspapers…)  can be left aside, in more usual office automation files: Word,  LibreOffice, InDesign…</p>
<p><strong>Example of plain text (restructuredtext format):</strong></p>
<pre><code class="lang-restructuredtext">Manuel du Maître de Jeu
############################

.. contents:: Table des Matières
    :depth: 2

Concept de la soirée mystère
================================

**Chrysalis:Mindstorm** est un huis clos entre `enquête 
&lt;https://fr.wikipedia.org/wiki/Enqu%C3%AAte&gt;`_ criminelle et conflit
géopolitique, où des agents secrets et des civils de divers pays se 
retrouvent face à un *redoutable* inspecteur de police, qui va les 
pousser dans leurs derniers retranchements.
</code></pre>
<p><strong>Rendering of this text once converted to PDF:</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*gzF6lDg3aFQ4cYiHogwlUw.png" alt="Image" width="800" height="281" loading="lazy"></p>
<p><strong>Step 2:</strong> Use a version manager for scenario files. This makes it possible to go  back in time at any time, to avoid horrifying accidental file  modifications, and to check the consistency of each of the changes made  (renaming a place, adding information for a group of players…).</p>
<p><strong>Viewing a change made to the rules of the game:</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*coZjpx5FCi1RERBRrOYIOQ.jpeg" alt="Image" width="800" height="421" loading="lazy"></p>
<p><strong>Step 3:</strong> Add  a small processing engine, to enrich the text with simple and practical  features: allow one file to include another, define reusable text  blocks, insert variables (e. g. the date of a crucial event, different  for each session of the mystery evening), display different information  according to the team to which the targeted player belongs….</p>
<p><strong>Step 4:</strong> Automate consistency checks. To do this, I created specific tags in the processing engine, which I then inserted as I wrote:<br> — The  {% fact %} tag is used to announce a fact (e. g. “so-and-so tried to  rob Loyd Georges”), and to indicate whether the player is the author or  just a witness.<br> — The {% hint %} tag allows you to request the existence of a physical clue (letter, object…) to give to the player.<br> — The  {% symbol %} tag ensures that a value is unique in all scenario files  (e. g. the exact time of a crime), while avoiding the use of “variables”  that obscure the text.</p>
<p><strong>Example of using special tags to enrich a scenario text:</strong></p>
<blockquote>
<p>To  the attention of {{agent_gamma_fake_name }} : the country of {% symbol  “Balberith” for “first_country_at_war” %} has entered the war, following  the plot led by agent Epsilon {% fact “agent_epsilon_triggered_war” as  author %}. You have a testimony signed by him and attesting to it. {%  hint “epsilon_signed_testimony_for_agent_gamma” is needed %}.</p>
</blockquote>
<p>As  we can see, these tags each have their own syntax, and can use other  features of the processing engine, such as variables (of which we have  an example with _{{agent_gamma_fake<em>name }}</em>).</p>
<p><strong>Step 5:</strong> Link  all this with scripts, which will automate the different steps of  creating game documents: gathering useful data (including player  photos), distributing scenario pages for each participant (global  context, personal context, game rules…), transforming them into PDF  format, and generating summary sheets for the game master.</p>
<p><strong>Some of the PDF documents generated, next to their plain text sources:</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*weQW6mnReW0hV9TNieakNw.png" alt="Image" width="783" height="554" loading="lazy"></p>
<h3 id="heading-the-future">The future</h3>
<p>All this is all well and good, but what about the rest of the role-playing  community? Can it benefit, more broadly, from the functionalities  offered by this scriptwriting support system?</p>
<p>The  answer is yes. However, as seen in the steps above, this tooling  requires a certain affinity with processes that are usually reserved for  computer development; an affinity that many GN authors do not have.</p>
<p>I  am therefore listening to authors tempted by the experience, in order  to see with them how they work, what formats and tools they are able to  use, and how this system could be generalized to suit their use. I could  then see to extract this code (which is currently strongly linked to  the structure of the Chrysalis game files), to make it more autonomous  and more easily deployable.</p>
<p>Note that software such as <a target="_blank" href="https://twinery.org/">Twine</a> already allows scenarios to be created in a fairly simple way, with a  mini-language to define variables and use logical operations. Pychronia  Tools machinery therefore only makes sense for the very high level of  integration it offers, with its automated consistency checks and  end-to-end generation scripts.</p>
<p>Interested in this scriptwriting support system? Feel free to contact me using the information on the <a target="_blank" href="https://chrysalis-game.com/fr/cms/contacts/">Chrysalis Website</a>.</p>
<h3 id="heading-appendix-details-for-computer-literate-people">Appendix : Details for computer literate people</h3>
<p>My machinery is based on the Python language and its document manipulation/creation ecosystem.</p>
<p>As  for the “plain text” format of the scenario, many “markup languages”  can be used for this purpose: restructuredtext, markdown, textile,  latex, or even html… I chose <a target="_blank" href="http://docutils.sourceforge.net/docs/user/rst/quickstart.html">restructuredtext</a> for  its clarity, versatility, and its advanced integration with the Python  language. To edit these text files, of course, Pycharm, Notepad++,  Geany, or a simple notebook can do the trick.</p>
<p>For  the version manager (or “VCS”), I chose Git and its excellent  TortoiseGit graphical interface (available under Windows only  unfortunately). Mercurial, Bazaar, DARCS, or others are just as  relevant. At a minimum, we can use the versioned file backup proposed by  Dropbox et al., even if it offers only a few features to visualize the  differences between several writing steps…</p>
<p><strong>Visualization, via GIT, of the sets of modifications made to the scenario:</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*GzYryvzHO22pB5UoanSL7Q.png" alt="Image" width="800" height="591" loading="lazy"></p>
<p>Regarding the template engine used to process text files (and for specific tags), finally, I have integrated the powerful <a target="_blank" href="http://jinja.pocoo.org/">Jinja2</a>,  which allows you to create variables and macros directly in the  templates. The data handled by this engine comes, in my case, from a  tree structure of Yaml files, but many other sources (python file, csv,  xml…) are very easily integrated.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Why you should do Augmented Reality if you are a JavaScript developer — and how to start ]]>
                </title>
                <description>
                    <![CDATA[ By Evaristo Caraballo If you are a JavaScript coder who is still late to making up a definitive list of resolutions for 2019, let me give you a hand: Start figuring out how to get into Augmented Reality (AR). The Augmented/Mixed/Virtual Reality (AR/M... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/an-intro-to-augmented-reality-for-the-javascript-developer-with-an-example-71875ab184ee/</link>
                <guid isPermaLink="false">66c343f242d4db64acf4cbc8</guid>
                
                    <category>
                        <![CDATA[ Augmented Reality ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Virtual Reality ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 11 Jan 2019 16:43:29 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*hIfbsKuXmHPb0qEKvotZ4A.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Evaristo Caraballo</p>
<p>If you are a JavaScript coder who is still late to making up a definitive list of resolutions for 2019, let me give you a hand: Start figuring out how to get into Augmented Reality (AR).</p>
<p>The Augmented/Mixed/Virtual Reality (AR/MR/VR) combo has enjoyed frenetic growth since 2016, coming from a <a target="_blank" href="https://www.statista.com/statistics/591181/global-augmented-virtual-reality-market-size/">marginal market value</a> of bit more than $6 Billion to one that might reach the $210 Billion in sales (including hardware) by 2022. Of all, <a target="_blank" href="https://www.telecompetitor.com/ar-vr-forecast-bad-quarter-good-future/">Augmented Reality</a> is the one experiencing steady growth.</p>
<p>At first, a JavaScript (web) developer wanting to get into the AR boat might feel discouraged when finding <a target="_blank" href="https://blog.pusher.com/how-you-can-become-an-ar-vr-developer/">the usual required skills</a>; and then there is who ask <a target="_blank" href="https://medium.freecodecamp.org/8-ways-ai-makes-virtual-augmented-reality-even-more-real-25037707cfa1">Machine Learning</a> or Internet of Things. However if you are mainly a JavaScript developer, consider yourself blessed: the language is <a target="_blank" href="https://www.quora.com/What-programming-language-is-used-to-create-virtual-reality-experiences-and-programs">recurrently mentioned</a> as one you should know to enter this sector. The reason? Right now <strong>a lot of AR development goes on the web</strong>. And this is where JavaScript reigns.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*m3EEXIiH_ZXxVzl-7-grLg.png" alt="Image" width="800" height="567" loading="lazy">
_Mobile and Web were the last getting AR capabilities, and are still developing (extract from a [buildAR presentation](https://www.slideshare.net/robman/web-standards-adoption-in-the-ar-market/6" rel="noopener" target="<em>blank" title="))</em></p>
<h3 id="heading-augmented-jobs-for-the-javascript-fan-really">Augmented Jobs for the JavaScript-fan — Really?</h3>
<p>Maybe not too fast. There are many examples where AR/MR/VR shines on its own, specially in niche markets, but the industry hasn’t completely figured out the full value of the technology for the general consumer. Once that it solved, the industry would be certainly making more AR/MR/VR products, which would translate into more jobs.</p>
<p>For some analysts AR is expected to have the most pervasive impact, in part because it doesn't require <a target="_blank" href="https://www.quora.com/What-are-some-good-VR-AR-hardware-devices-that-not-many-people-know">specific devices and conditions</a> to be implemented as VR does.</p>
<blockquote>
<p>AR has utility for almost everything, overlaying useful and relevant information on the world around you. AR can be pervasive in a way that VR cannot.</p>
<ul>
<li>David McQueen -Strategy Analytics- <a target="_blank" href="https://www.twice.com/product/ces-2018-how-disruptive-can-vr-ar-become-roundtable-discussion">from an interview on Twice</a></li>
</ul>
</blockquote>
<p>It rests on the industry to find how to make AR a more every day life tech. According to some companies, particularly within the mobile phone realm, exploiting better the AR potential reduces to a well-known rule: <em>SIMPLICITY</em>.</p>
<blockquote>
<p>While Unity has become the default path for building AR apps, an increasing number need only a sprinkling of AR.</p>
<ul>
<li>from an <a target="_blank" href="https://medium.com/homestory-ar/building-an-ar-ai-furniture-app-with-react-native-1847bc1fcbaa">article</a> by <a target="_blank" href="https://www.freecodecamp.org/news/an-intro-to-augmented-reality-for-the-javascript-developer-with-an-example-71875ab184ee/undefined">Benjamin Devine</a>, Homestory AR</li>
</ul>
</blockquote>
<p>In many cases, resourcing onto the leading AR tools could be an overkill. Instead, a bunch of good UX-driven features over some 2D/3D assets could be more than enough to make striking products. Something a JavaScript developer regularly does.</p>
<p>It is then possible that any JavaScript developer will be embedding (non)standard AR/VR features as an extension of their traditional duties in the future. And if required, JavaScript is robust enough for more complex tasks. The sky is the limit.</p>
<h3 id="heading-becoming-javascript-augmented">Becoming JavaScript-Augmented</h3>
<p>Before starting, I would suggest to have a look at the several AR platforms and standards. The same technical constraints affecting the industry are also reflected in the AR world.</p>
<p>For example, there are several platforms, one for each Big Tech (Google = <a target="_blank" href="https://developers.google.com/ar/discover/">ARCode</a>, Apple = <a target="_blank" href="https://developer.apple.com/arkit/">ARKit</a>, MS = <a target="_blank" href="https://github.com/microsoft/chakracore">ChakraCore</a>, Facebook = <a target="_blank" href="https://developers.facebook.com/blog/post/2018/05/01/ar-studio-create-distribute/">AR Studio</a>, <a target="_blank" href="https://facebook.github.io/react-360/">React 360</a>, Mozilla = <a target="_blank" href="https://aframe.io/">aframe</a>).</p>
<p>After having a quick look at the options, starting fully JavaScript’ed Augmented Reality projects is relatively easy. You can begin by taking any web/app dev framework like <a target="_blank" href="https://cordova.apache.org/">Cordova</a>, <a target="_blank" href="https://ionicframework.com/">Ionic</a>, <a target="_blank" href="https://facebook.github.io/react-native/">React Native</a> or <a target="_blank" href="https://vue-native.io/">Vue Native</a> to embed the AR framework of your choice — and deploy 3D assets on top of the real world.</p>
<p>If what you want is to deploy on the web using mostly marker-based AR, you could use GitHub repos like <a target="_blank" href="https://github.com/jeromeetienne/AR.js">AR.js</a> (free), <a target="_blank" href="https://www.argonjs.io/">argon.js</a> (free but limited) or <a target="_blank" href="https://awe.media/">awe.js</a> (paid PaaS but with an old GitHub repository still available). There are a few tailored ones that are harder for the novice, many of them focused on things like facial/head recognition (such as <a target="_blank" href="https://trackingjs.com/">tracking.js</a> and <a target="_blank" href="https://github.com/auduno/headtrackr">headtrackr</a>).</p>
<p>Or you can boost your project capabilities if you are able to port available SDKs made by <a target="_blank" href="http://socialcompare.com/en/comparison/augmented-reality-sdks">AR-related companies</a>. There are many APIs that render as AR on browser too. For example, <a target="_blank" href="https://www.mapbox.com/">Mapbox</a> follows that path and it is developed on JavaScript.</p>
<p>I would suggest you to keep it simple but interactive.</p>
<p>However if your ambitions point to also mastering design and animation in JavaScript, you definitively have to learn at least one <a target="_blank" href="https://1stwebdesigner.com/3d-javascript-libraries/">3D Javascript package</a>, and <a target="_blank" href="https://threejs.org/">THREE.js</a> the most popular. Wait, though, until you have gained a good base of JavaScript and <a target="_blank" href="https://www.opengl.org/">OpenGl</a> as well as geometry, trigonometry, linear algebra or physics. And don’t expect more help from the existing 3D JS GUIs; in particular, THREE.js has none. Challenging but exciting!</p>
<h3 id="heading-bonus-example">Bonus Example</h3>
<p>I wanted to prepare a quick demo just to explore the technology, so I took a nice CodePen and modified it to fit a marker-based web-rendered AR animation ported within a clone of <a target="_blank" href="https://github.com/stemkoski/AR-Examples">Stemkoski's</a> <a target="_blank" href="https://stemkoski.github.io/AR-Examples/">great work</a> with AR.js.</p>
<p>For you to see the example you need <em>a mobile device with a camera</em> and internet (phone or tablet), and either a printed copy of <em>the marker</em> or another device to show it on screen.</p>
<p>Ready? Now open this <a target="_blank" href="https://evaristoc.github.io/ARexample/">link</a> using a browser in your mobile device:</p>
<p><a target="_blank" href="https://evaristoc.github.io/ARexample/">https://evaristoc.github.io/ARexample/</a></p>
<p>Give authorization to use your camera, and point the camera <strong>to a marker like below</strong>, either printed or in another screen.</p>
<p><strong>NOTE:</strong> works on Android and Chrome — it might not work for other devices and browsers ?.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*7g9MciK6LR-9VRcH-LePSQ.png" alt="Image" width="512" height="512" loading="lazy">
_The original animation can be found [here](https://codepen.io/rainner/details/LREdXd" rel="noopener" target="<em>blank" title=").</em></p>
<p>Happy New Year!</p>
<p>I hope you will find this technology as fascinating as I do. If so, don't stay alone: contact us at the <a target="_blank" href="https://www.freecodecamp.org/forum/">freeCodeCamp forum</a> and share your questions and ideas.</p>
<p>And if you liked this article, don't forget to give it a ? and to share it on social media.</p>
<p>Thanks for reading, enjoy AR and Happy Coding!!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Tools I wish I had known about when I started coding: Revisited ]]>
                </title>
                <description>
                    <![CDATA[ By Mario Hoyos A few days ago, I wrote this article for freeCodeCamp which has since gone on to be read more times than I could have ever imagined. The response from the community has been overwhelming, humbling, and inspiring. With the intent of spr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/tools-i-wish-i-had-known-about-when-i-started-coding-revisited-ffb715ffd23f/</link>
                <guid isPermaLink="false">66c3637940438b5931fe099a</guid>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sun, 25 Feb 2018 10:59:16 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*mM2AMk0TRENA2zF2RMEebA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mario Hoyos</p>
<p>A few days ago, I wrote <a target="_blank" href="https://medium.freecodecamp.org/tools-i-wish-i-had-known-about-when-i-started-coding-57849efd9248">this article for freeCodeCamp</a> which has since gone on to be read more times than I could have ever imagined. The response from the community has been overwhelming, humbling, and inspiring.</p>
<p>With the intent of spreading that article as far and wide as I could, I posted it to <a target="_blank" href="https://www.reddit.com/r/learnprogramming/">r/learnprogramming</a> thinking that some of the aspiring programmers there might find it helpful. <a target="_blank" href="https://www.reddit.com/r/learnprogramming/comments/7y08m9/tools_i_wish_i_had_known_about_when_i_started/">The post can be found here</a>.</p>
<p>I thought that post might get banned for self-promotion, but what followed instead was a flood of tool recommendations. Since the last article proved useful to at least a few of you, I went ahead and organized the suggestions from that thread for those that are operating-system agnostic, Mac specific, and Windows specific. Now, before the Linux folks get all up in arms, there are versions of most of these available for you, too. We can all win.</p>
<p>I considered including the usernames of the Redditors that made the recommendations, but since I am not sure if they appreciate being called out, I will assume all responsibility for any tool you find sub-par.</p>
<p>Feel free to jump around.</p>
<h3 id="heading-operating-system-agnostic">Operating System Agnostic</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/jgfNtMJ5HmM0YngijlMCvjx-NotAOgBkdcfU" alt="Image" width="800" height="533" loading="lazy">
<em>Photo courtesy of Pexels.com</em></p>
<ul>
<li><a target="_blank" href="https://www.getpostman.com/">Postman</a> — At its most basic, this is an application that allows you to test the different endpoints of an API. You know, that good CRUD stuff. Once you dive deeper into it, you will find it is also incredibly useful for writing up test suites and much more.</li>
<li><a target="_blank" href="https://insomnia.rest/">Insomnia</a> — This is an open-source alternative to Postman. It appears to have a lot of the same functionality with a slightly lower price tag for their premium packages. Between this and Postman, the choice seems to mostly be personal preference for the UX. Don’t stay up all night making a decision between the two (I had to, forgive me).</li>
<li><a target="_blank" href="https://chrome.google.com/webstore/detail/restlet-client-rest-api-t/aejoelaoggembcahagimdiliamlcdmfm?hl=en">Restlet</a> — If you don’t want to download either of the above, this is an API testing client that is available as a Chrome extension. It even allows the importing of Postman tests and several other formats.</li>
<li><a target="_blank" href="https://www.charlesproxy.com/">Charles</a> — This is a sweet web debugging proxy that allows you to view all of the information you could possibly want to see about the requests being made between your machine and the internet. It basically turns you into a hacker, allowing you to check your website for security vulnerabilities and such.</li>
<li><a target="_blank" href="https://www.telerik.com/fiddler">Fiddler</a> — Basically the same tool as Charles. Personal preference seems to be the main deciding factor between the two, though both can greatly increase your debugging/troubleshooting capabilities.</li>
<li><a target="_blank" href="https://justgetflux.com/">Flux</a> — If you’re reading this, chances are you probably spend a ton of time on your computer. Flux is a program that turns the blue light down on your screen as the sun sets, with the hopes of mimicking the day/night cycle and helping you sleep better. Not as good as getting time away from the screen, certainly, but it’s better than nothing!</li>
</ul>
<h3 id="heading-mac-specific">Mac Specific</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/KriF1-ZyXtIxyBg3drw9Ly5seIjvnLTNeaqQ" alt="Image" width="800" height="531" loading="lazy">
<em>Photo courtesy of Pexels.com</em></p>
<ul>
<li><a target="_blank" href="https://paw.cloud/">Paw</a> — A competitor to both Postman and Insomnia above, Paw is a similar piece of software that feels more like a native Mac application than the others. If you love that Mac feel, this might be the API tool for you.</li>
<li><a target="_blank" href="https://kapeli.com/dash">Dash</a> — With over 200 documentation sets to search through, Dash is an offline goldmine of documentation for the developer who loves to read documentation so much that they do it even when the internet is out.</li>
<li><a target="_blank" href="http://www.bear-writer.com/">Bear</a> — Not directly coding related, but a neat piece of software for taking notes if that is something you do in your day-to-day. I can’t help but wish that this would be integrated with Paw somehow, for pun-related purposes.</li>
<li><a target="_blank" href="https://itunes.apple.com/us/app/magnet/id441258766?mt=12">Magnet</a> — Okay. Fair warning: This one costs money. If you can afford to dish up the 99 cents, however, I think it is worth it for those tough times when you don’t have access to a second monitor. This tool allows you to easily snap different windows to separate areas of your screen so you can run multiple terminals at once and tell your mom you are a hacker. (They are not paying me for this recommendation, sadly. However, if you are the creator of Magnet and you wish to pay me, please get in touch).</li>
</ul>
<h3 id="heading-windows-specific">Windows Specific</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/zkgETlaLAEBbkdcoPw4YN2DjN-pwCnT-H0Cn" alt="Image" width="800" height="533" loading="lazy">
<em>Photo courtesy of Pexels.com</em></p>
<ul>
<li><a target="_blank" href="https://conemu.github.io/">ConEmu</a> — If you are on Windows and you would like to have multiple terminal tabs, this is an easy, free solution to your tab-related woes. Never again will you have to flip back and forth through entire windows just to look at your different error messages.</li>
<li><a target="_blank" href="http://ditto-cp.sourceforge.net/">Ditto</a> — You know when you’re trying to copy four separate answers from Stack Overflow and you have to keep switching back and forth from your browser to your IDE? Worry no more. With the Ditto clipboard manager, you can copy-paste your way to victory.</li>
<li><a target="_blank" href="http://martin-stone.github.io/touchcursor/">TouchCursor</a> — Programmers of all sorts are notorious for wanting to use the mouse as little as possible. If you find yourself in that camp, then this will be right up your alley.</li>
<li><a target="_blank" href="http://cmder.net/">Cmder</a> — I am going to forego my description of this product, as I feel the description on their website is much better — “Cmder is a software package created out of pure frustration over the absence of nice console emulators on Windows. It is based on amazing software, and spiced up with the Monokai color scheme and a custom prompt layout, looking sexy from the start.” I don’t know about you, but I am a fan of any software that describes itself as “sexy”.</li>
<li><a target="_blank" href="https://www.gpsoft.com.au/">Directory Opus</a> — This is basically what Windows Explorer should have been from the start. Opus allows you to dig through your poorly-organized documents almost as if they were well-organized.</li>
<li><a target="_blank" href="https://zealdocs.org/">Zeal</a> — With over 200 documentation sets to search through, Zeal is an offline goldmine of documentation for the developer who loves to read documentation so much that they do it even when the internet is out. (Copy-pasted from Dash in the Mac section).</li>
</ul>
<h3 id="heading-honorable-mention">Honorable Mention</h3>
<p>If you are feeling particularly sadistic, throw out your current IDE and learn to use <a target="_blank" href="http://www.vim.org/">Vim</a>, <a target="_blank" href="https://www.gnu.org/software/grep/manual/grep.html">Grep</a>, <a target="_blank" href="http://zsh.sourceforge.net/">Zsh</a>, and/or <a target="_blank" href="https://beyondgrep.com/">ack</a>. From what I hear, these can super-charge your productivity once you get over the formidable learning curve. However, I don’t hate myself enough to find out.</p>
<h3 id="heading-in-conclusion">In Conclusion</h3>
<p>As I’ve mentioned before, there is almost always more than one way to do any given thing. And it is more important to learn to use the tools you already have, rather than to go installing things willy-nilly with the hopes that it will somehow make you a <a target="_blank" href="https://leetcode.com/">Leet</a> coder.</p>
<p>For example, if you go to the <a target="_blank" href="https://www.reddit.com/r/learnprogramming/comments/7y08m9/tools_i_wish_i_had_known_about_when_i_started/">post I made it on Reddit</a> you’ll see there are 26 comments about tools to pick colors alone. I almost included them here but having a whole subsection for color-pickers seemed excessive.</p>
<p>That being said, I’m a sucker for some sweet tools, so if you have any that you can’t live without please leave them in the comments below!</p>
<p>If you liked this article please give it some claps and check out:</p>
<ul>
<li><a target="_blank" href="https://codeburst.io/how-i-cut-my-react-javascript-bundle-size-in-half-with-three-lines-of-code-fe7798ecbd3f">How I cut my javascript bundle in half with three lines of code</a></li>
<li><a target="_blank" href="https://codeburst.io/how-i-decreased-the-size-of-my-heroku-app-by-75-1a4cf329b0ab">How I decreased the size of my Heroku app by 75%</a></li>
<li><a target="_blank" href="https://byrslf.co/life-is-too-short-to-be-miserable-and-why-i-left-a-six-figure-job-in-the-search-for-something-327ab84416a9">Why I left a six-figure job in the search for something better</a></li>
</ul>
<p>Also, give me a follow on <a target="_blank" href="https://twitter.com/marioahoyos">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
