<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Livefront - Medium]]></title>
        <description><![CDATA[Thoughts from the Livefront team - Medium]]></description>
        <link>https://medium.com/livefront?source=rss----75a105744d9a---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Livefront - Medium</title>
            <link>https://medium.com/livefront?source=rss----75a105744d9a---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 25 Jun 2026 07:51:29 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/livefront" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Identity Crisis]]></title>
            <link>https://medium.com/livefront/identity-crisis-f135cdc4544d?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/f135cdc4544d</guid>
            <category><![CDATA[ux]]></category>
            <category><![CDATA[strategy]]></category>
            <category><![CDATA[gamification]]></category>
            <category><![CDATA[language-learning]]></category>
            <category><![CDATA[product-strategy]]></category>
            <dc:creator><![CDATA[Phillip Johnson]]></dc:creator>
            <pubDate>Mon, 15 Jun 2026 17:49:31 GMT</pubDate>
            <atom:updated>2026-06-15T17:49:30.285Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Abv-ZQJYOKK9aJ2je7I58Q.png" /></figure><p>Welcome back to Second Drafts, where we dissect products that either aced the product strategy or perhaps misstepped. In the former case, we discuss what they did right. In the latter, we propose what could or should change. This article is the latter. We recognize it’s easy to critique, but that doesn’t mean there’s no value in it. Let’s uncover that value together.</p><h3>Field Notes Chapter Five: Duolingo</h3><p>Duolingo, the world’s biggest language learning app, was founded in 2011 by a Carnegie Mellon professor, Luis von Ahn, and his graduate student, Severin Hacker. Their original mission was “to provide free language education to a global audience.” Today, they remain the CEO and CTO, respectively, and their new mission is “to develop the best education in the world and make it universally available.”</p><p>Their growth path has been long and mostly steady, their final fundraise a $30M Series F in 2019 that gave the company a $1.5B valuation, representative of its 30 million monthly active users and 300+ million registered users. In 2021, Duolingo IPO’d.</p><p>Since then, DUOL is down 25%, but that doesn’t tell the full story. In mid-2025, it reached an all-time high (up 280% from IPO) fueled by AI hysteria — von Ahn publicized that course creation accelerated <em>by more than an order of magnitude</em> due to AI, and that AI-driven personalization was boosting retention. Their stock reached a price-to-earnings multiple of 270x. For context, Nvidia is currently 46x. Since then, DUOL plummeted 73%, largely driven by the deceleration of user growth, worries that AI translation would undermine Duolingo’s value prop, and the simple fact that a correction was in order.</p><p>So where does that put Duolingo today? For starters, they remain a linear language learning app, now offering 40+ languages including music, math, and chess. Their original freemium model has established two paid tiers — the ad free version called “Super” and Duolingo Max, which offers additional AI-created learning content.</p><p>But what is Duolingo, really? Duolingo’s CEO said in an interview that when engagement and learning outcomes are at odds,<a href="https://www.theverge.com/24267841/luis-von-ahn-duolingo-owl-language-learning-gamification-generative-ai-android-decoder"> they prefer to go for engagement</a>. So is the app really about learning?</p><p>Is Duolingo education, or is it gaming? Can it be both? On that point, who are they really competing with — the other top language platforms, or Wordle?</p><p>Is what they are saying different from what they are doing? If so, does that matter? If there’s a disconnect between their brand perception and their value prop, could that actually serve them?</p><p>Lastly, what is the cost of a mismatch between product strategy and brand perception?</p><h3>The Duolingo Dilemma: A Strategy of Misidentification by Paige Keane, Product Strategist</h3><p>It’s Christmas time, and my aunt and uncle are on the couch in the living room. The ethereal “duh-ding” of a Duolingo exercise well-done can be heard periodically from across the house. When I enter the living room, my aunt and uncle share, grinning and giggling, that they have decided to learn Spanish. The thing is, they’re not going to learn Spanish by using Duolingo — at least not what most people picture when they imagine learning Spanish (fluency or proficiency). I know this because I have learned three languages to fluency. So what are they <em>really</em> doing?</p><p>Much to the exasperation of the polyglot community, Duolingo’s stated mission is to “develop the best education in the world.” The brand positions itself primarily as a language-learning platform. However, there is a fundamental gap between the brand’s marketing and its functional reality. Many of us sense (and polyglots know) that we can’t actually achieve our language learning dreams on Duolingo beyond some listening and reading for beginners, no matter how long our streak may be. That manipulative owl!</p><p>Duolingo’s true competitors are stimulating daily rituals like Wordle or the NYT Crossword, family fun games like GlobeGuessr and trivia, or brain training games like Elevate or Lumosity. It is a family-friendly diversion for intellectually curious people who want to feel productive while they play. Its true strengths lie in habit formation, gamification, and cosmopolitanism <em>lite</em>. The Duolingo user isn’t looking for the frustrating, embarrassing, time-obliterating (but rewarding) work of actual language learning; they are looking for a brain training game with a sprinkle of learning. And what’s wrong with learning the Japanese word for rice when you have five minutes to spare?</p><p>The recent expansion into math, music, and chess suggests an awareness that the platform must evolve to include more games, but these subjects feel random. They lack the worldliness that defines the Duolingo brand. An expansion into world trivia, geography, or travel-themed games would be more cohesive. Such moves would resonate with their audience of global citizens, people who are motivated by a curiosity about the world.</p><p>Duolingo now faces a critical inflection point: the need to further monetize without a cohesive identity. By investing heavily in AI for deep learning and Max tiers, they appear to be chasing the serious learner demographic. However, the core product remains optimized for engagement, not outcomes. This creates a mismatch — the experience is becoming too demanding for the casual gamer, yet remains too passive for the serious student. To truly shift from engagement to education, Duolingo would require a total overhaul from passive to active learning.</p><p>There is undeniable commercial genius in selling the fib of effortless language learning. It reminds me of Gwyneth Paltrow’s <em>Goop. </em>Paltrow’s lifestyle and wellness company thrives by selling aspirational products and the perception of access to the “wellness elite” with crystal-infused beauty products that defy medical science. Duolingo similarly thrives by selling the aspiration to learn a language and join the “polyglot elite” with the fantasy of worldliness and effortless bilingualism. Paying over $1000 for a pair of shorts does not bring me closer to being Gwyneth Paltrow and playing a Duolingo game 5 minutes a day will not make me a globetrotting polyglot, but the feeling of progress towards that aspiration feels good to many.</p><p>So what’s the harm in refusing to embrace its true identity? Duolingo risks alienating both of its core user bases. By straddling the fence, Duolingo wastes vast resources trying to force their brain games to feel like “deep learning,” which only dilutes the fun for casual users. Simultaneously, they try far too little to provide actual, rigorous education for serious students. They are left with an identity crisis that is both an ineffective classroom and a boring arcade.</p><p>To continue its viral growth, Duolingo could choose one of two distinct paths:</p><p><strong>Fully embrace the brain-game identity</strong>: Free the brand from the guise of an educational platform and lean into smart, family-friendly games with globalist themes (geography, culture, history).</p><p><strong>The Chess.com pivot</strong>: Shift focus entirely toward actual language learning outcomes. Follow the Chess.com model, where a game serves as the gateway to a deeply rigorous, proficiency-based ecosystem that rewards mastery over mere streaks.</p><p>Great product strategy requires taking an honest inventory of strengths and weaknesses, making a definitive choice, and taking cohesive action. Duolingo must decide if it wants to be the world’s most popular purveyor of culturally inspired games or to fulfill its stated mission of developing the best education in the world. Failing to choose means remaining a closet full of clothes with absolutely nothing to wear.</p><p>If you like reading about product strategy and design, be sure to check out the other installments in our series <a href="https://livefront.com/writing/second-drafts/">here </a>. <strong>If you’re looking for a digital product expert like Paige to help your organization get to the next level, please </strong><a href="https://livefront.com/contact/"><strong>reach out</strong> </a><strong>now!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f135cdc4544d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/identity-crisis-f135cdc4544d">Identity Crisis</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[QA Quonversations: AI’s Place in Software Testing]]></title>
            <link>https://medium.com/livefront/qa-quonversations-ais-place-in-software-testing-b76366e11a84?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/b76366e11a84</guid>
            <category><![CDATA[qa-engineer]]></category>
            <category><![CDATA[qa-testing]]></category>
            <category><![CDATA[qa-automation]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[qa]]></category>
            <dc:creator><![CDATA[John Caplinger]]></dc:creator>
            <pubDate>Mon, 01 Jun 2026 18:49:22 GMT</pubDate>
            <atom:updated>2026-06-01T18:49:20.708Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rXvDi67x0SV2rQQpXCY4Nw.png" /></figure><p>At Livefront, the Quality Assurance team likes to get together every couple of weeks for a “QA Quorum” to talk about project updates, interesting bug finds, tactics we’re experimenting with, and new things in the world of QA. One of the topics we recently discussed was how we use AI in our day-to-day work, and we thought, “This would be a good conversation to share with others!” So, welcome to the first of what we like to call, “QA Quonversations.”</p><p>Joining this conversation were Livefront QA team members: Ben Kimball, Haley Bowler, Lauren Whitesell, Lukus Cich, Jeremy Eventyr, John Caplinger, and Sonia Merten. The team has a combined experience of 30+ years in the Quality Assurance field, focused on API testing, UI/UX testing, accessibility testing, manual testing, and test automation.</p><p>We started the conversation with the question:</p><h3>What are some ways you are using AI for Quality Assurance?</h3><p>Across our QA team, AI has become an everyday tool that helps us think through problems, write better documentation, broaden our test coverage, and troubleshoot tricky technical issues. All of us have unique use-cases depending on the client we’re working with, but each of us have found ways to make it a meaningful part of our workflow.</p><p>Ben said he often leans on AI at the beginning of his process to help him get his thoughts moving. He likes to use AI for “rubber-ducking” problems he may be running into and to help bounce around ideas. “Usually the responses I get back will help me form my thoughts and move down to whatever workflow I want to go,” he said. That same approach has helped him with other tasks like creating a tutorial on merge conflicts, where he walked through the challenge with ChatGPT until he found a reliable method.</p><p>Jeremy said, “I’ve just started using Cursor and I’ve found it really useful for writing documentation.” He realized that he could ask Cursor to analyze his automation tool repo’s code and generate a Readme file for the repository. “It went through the repo, analyzed the tools, then gave some short descriptions and some examples of how to use them.” He said there was quite a bit of editing necessary afterwards, but it was a solid basis to start coming up with the documentation for the code.</p><p>Lauren works on an open-source project where a “description of work” is required when approving a ticket. “I will take the contents of the ticket before I start testing and ask AI to provide a list of scenarios to cover.” She said this helps her catch any test cases she might not have initially thought of and guides her on what she will be testing. She will also use AI to help write up the required comment once she is done testing the ticket. “That’s the biggest thing I use it for day-to-day.”</p><p>Sonia mentioned, “I feel like this is an obvious one, but utilizing Copilot when writing automated tests. It’s been really helpful with refactoring some of my tests by making them more efficient, generally making the code better, and helping me understand the code better too!” She also gave a shout-out to Ben for showing her how to use AI to summarize what has changed in a current PR instead of having to write it manually.</p><p>Lukus also works on an open-source project where there is a lot of complexity. “This may be a one-off situation, but I’ve found it really useful for creating diagrams.” He said there was a complex flow for a particular test coverage area, “so I fed it some information and got a super helpful diagram that I could share with other QA folks on the team and it helped our testing a lot!” And when he needed a simulated rooted Android device and ran into issues the forums couldn’t solve, he turned to ChatGPT. “It actually ended up helping me get to where I needed to go!”</p><h3>What are other things do you find AI useful for?</h3><p>John finds AI tools helpful for taking meeting notes, especially for gathering details he might have missed. “That’s definitely been a useful tool for me!”</p><p>Sonia has found it very useful for understanding particular accessibility guidelines. And the information provided allows her to be sure her testing is accurate. “It can point me to the WCAG (Web Content Accessbility Guidelines) and I don’t have to spend time searching for the information I need.”</p><p>John chimed in, “Using it to compare things is really handy too!” He works on a project that includes both a web and native app. To be sure the test suites were the same for each platform, he fed ChatGPT both test suites and prompted it to check the differences. “It was better than one of those ‘spot the diff’ programs because some test cases were named differently, but the AI was smart enough to figure out they were the same test!”</p><p>Ben was working on a spreadsheet project at the time and found it useful for providing Excel or Google Sheets formulas. “For example, combining two strings and then comparing them to something on a different worksheet, it’s really good at that!” Determining SQL queries or JQL queries for Jira is another thing he likes to use it for.</p><h3>What is your process for creating test cases using AI?</h3><p>John described a progressive prompting approach. He starts with broad context, then gradually refines prompts with specific requirements and scenarios — such as device orientation, authentication state, or poor network conditions. “Afterwards I’ll manually refine the list of test cases provided into what I feel are the appropriate tests to test.”</p><p>Lukus takes a different route, “I’ll create my first pass of test cases and then feed those in and ask it if there are any other scenarios that I didn’t consider.” While not all suggestions he received were useful, the exercise helped surface edge cases he hadn’t thought of.</p><p>Lauren said she follows a similar approach as John, but since the project uses Gerkin-styled test cases she says, “I’ll ask for the test cases to be written in that style. Then I’ll edit the test cases since it usually provides test cases that aren’t necessary or splits them up in a way I don’t like.” She then continues to iterate and refine her prompts until the test cases meet her standards.</p><p>Sonia voiced some concern about using AI to create test cases. “I find it’s a little verbose whereas I like to make my test cases as concise as possible.” For her, AI is better suited to identifying gaps than generating test artifacts.</p><p>Lauren and Ben both felt there were ways to fine tune the AI’s responses. Lauren said, “I’ve had some success with prompts like ‘make this more concise’ but the results can still be hit or miss.” Ben agreed, “you could tell [the AI] to always give you test cases in an action-expectation format or Gerkin and it should stick to those instructions.”</p><p><em>Editor’s Update: Since the time of our conversation, we have started our AI automation process for test case creation and the results are very promising!</em></p><h3>What are some ways we could improve our AI results?</h3><p>At Livefront, one of the company mantras is, “I leave things better” which is why we wanted to discuss this question. Lukus brought up the idea that we could create a repository with the list of prompts that we know work well.</p><p>Jeremy responded, “That would be nice if we had some sort of library that you could use to say, here’s the prompts you could use to create a readme file or here’s the prompt you could use to look for edge-cases in this test scenario.” We all agreed!</p><h3>Are there things you’ve found that AI consistently messes up?</h3><p>Ben chimed in, “Until about 40 minutes ago I thought it didn’t know what animals were, but it turns out I don’t know what animals are!” He was working on creating an array to select a random animal and the AI included animals like “Zorilla” and “Zorse”. “…but it turns out those are real animals!” he exclaimed. John replied, “Ha! I didn’t know those were animals either!”</p><p>Jeremy pulled us back on track and said, “One of the things we’ve seen consistently with Copilot or Cursor is it’s solutions a) have hallucinations and come up with functions that don’t exist or b) it will generate code that is more complicated than it needs to be.” We all agreed, it is still important to know the coding syntax you are asking for help with in order to parse out these types of AI mistakes.</p><p>Ben mentioned he’s had a lot of issues when using Copilot when writing tests where the AI would edit the code in places he didn’t ask it to. “I’ve had to move to a ‘don’t do anything unless I say yes’ mindset with it and that works pretty well,” he said.</p><p>Haley remarked that she has trouble getting Copilot to provide useful responses. She asked Ben, “are there certain settings you’re using to control that?”</p><p>Ben described a process of creating an “.instructions” markdown file inside of “/.github/prompts/”. “That ‘.instructions’ file is the best thing you can do for yourself to use Copilot,” he said.</p><h3>Our conclusions</h3><p>The consensus we’ve arrived at is that AI is a helpful tool for troubleshooting, repetitive tasks, summarizing and documenting tests, and filling in gaps in our test suites. The things that you could teach almost anyone, but the AI learning curve is off-the-charts.</p><p>On the flip-side, we as humans still need to be vigilant about the data we’re feeding into the models and the trust we have in the results we get back. Blindly accepting what AI suggests only proves the limits of our own skills and inconsiderate prompts will only give you inconsiderate solutions.</p><p>What are your thoughts? Where do you find AI useful in your day-to-day? What do you watch out for when using it? Let’s keep the conversation going!</p><p>We have many more topics to discuss as a Quality Assurance team, so stay tuned for more QA Quonversations!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b76366e11a84" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/qa-quonversations-ais-place-in-software-testing-b76366e11a84">QA Quonversations: AI’s Place in Software Testing</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[You might have a strategy problem if…]]></title>
            <link>https://medium.com/livefront/you-might-have-a-strategy-problem-if-23c71d2f972f?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/23c71d2f972f</guid>
            <dc:creator><![CDATA[Jennifer Morgan]]></dc:creator>
            <pubDate>Mon, 01 Jun 2026 16:28:01 GMT</pubDate>
            <atom:updated>2026-06-01T16:27:54.891Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xso2n_z09VGyYaD4p-vqmw.png" /></figure><p>I’ve worked with teams that felt incredibly busy; roadmaps full, meetings nonstop, and everyone working hard. But progress still felt slower than it should.</p><p>That’s usually when people assume they have a delivery problem. Maybe a prioritization problem. Maybe a communication gap. But in many cases, the real issue isn’t execution. It’s strategy.</p><p>Strategy problems rarely show up labeled as “strategy.” They usually show up as everyday frustrations; things that feel operational, but never quite get better no matter how much process you add.</p><p>Here are a few signs your team might actually be dealing with a strategy problem (even if no one is calling it that).</p><h3>You might have a strategy problem if…everything feels like a priority</h3><p>Your roadmap is full. Your backlog is long. Every initiative feels important.</p><p>And yet, nothing seems to move as quickly as it should. Most teams interpret this as a prioritization issue: <em>“We just need to rank things better.” </em>But prioritization only works if there’s a clear strategy underneath it. Without that, you’re not really prioritizing, you’re negotiating.</p><p><strong>Example:</strong><br>A leadership team identifies five “top priorities” for the quarter. Each one makes sense on its own, and no one wants to deprioritize anything. The team ends up working across all five at once, but progress feels slow across the board. Not because the team isn’t working hard, but because focus was never clearly established.</p><h3>You might have a strategy problem if…work keeps getting redone</h3><p>Projects start with momentum, but direction shifts halfway through.</p><p>Design revisits decisions. Engineering rebuilds things. Stakeholders change expectations once they see something tangible. It’s easy to assume this is a planning issue: <em>“We should have thought this through more upfront.” </em>But more planning doesn’t fix a lack of shared understanding about the problem you’re solving, or the direction you’re heading.</p><p><strong>Example:</strong><br>A team builds an initial version of a feature based on early assumptions. Once stakeholders see it, they realize it doesn’t actually solve the right user problem. The team pivots, redesigns, and rebuilds, not because they moved too fast, but because the original problem wasn’t clearly defined.</p><h3>You might have a strategy problem if…decisions feel slow or keep getting revisited</h3><p>The same topics keep coming up.</p><p>Decisions get escalated or delayed. Or quietly revisited weeks later. Many teams treat this as a decision-making issue: <em>“We need clearer ownership.” “We need better frameworks.” </em>But a strong strategy acts as a filter for decisions. It gives teams a way to evaluate tradeoffs and move forward with confidence. Without it, every decision feels heavier than it should.</p><p><strong>Example:</strong><br>A team debates whether to build Feature A or Feature B. Both seem valuable. Without clear strategic priorities, the discussion drags on and resurfaces again later when priorities shift.</p><h3>You might have a strategy problem if…every conversation turns into a negotiation</h3><p>Not formal negotiation, but subtle negotiation about priorities, scope, and direction.</p><p>Every new request requires discussion. Every tradeoff feels political. Every decision feels like it needs consensus. This often happens when tradeoffs haven’t been made explicit. Without a strong strategy, teams default to negotiation because there’s no shared foundation to guide decisions. Over time, that slows everything down. Not because people disagree, but because there’s no clear direction to anchor decisions.</p><h3>You might have a strategy problem if…teams feel misaligned (even when you’ve “aligned”)</h3><p>Everyone was in the meeting. The roadmap was shared. The plan was documented.</p><p>And yet, as work progresses, teams start moving in slightly different directions. Most teams assume this is a communication problem: <em>“We need to communicate more clearly.” </em>But alignment isn’t just about sharing information; it’s about shared understanding. And shared understanding is hard to build when the underlying direction isn’t clear.</p><p><strong>Example:</strong><br>A roadmap is presented across departments. Marketing interprets priorities one way. Engineering focuses on something slightly different. Customer support prepares for something else entirely. Everyone heard the same message but understood it differently.</p><h3>You might have a strategy problem if…your team feels busy, but not effective</h3><p>There’s no shortage of activity:</p><ul><li>Meetings</li><li>Work in progress</li><li>Status updates</li></ul><p>But progress still feels slower than it should.</p><p>It’s tempting to interpret this as an efficiency issue: <em>“We need fewer meetings.” “We need to streamline.” </em>But the problem often isn’t how much you’re doing, it’s how focused that effort is. When the strategy is unclear, effort gets scattered.</p><p><strong>Example:</strong><br>A team moves quickly between multiple initiatives, responding to new requests and shifting priorities. Work gets started, paused, and restarted. Everyone is working hard, but momentum never really builds.</p><h3>What all of this has in common</h3><p>None of these feel like strategy problems.</p><p>They feel like:</p><ul><li>prioritization issues</li><li>communication gaps</li><li>delivery challenges</li></ul><p>So teams try to fix them at that level.</p><p>They add process.<br>They introduce new tools.<br>They optimize workflows.</p><p>And yet, the friction doesn’t go away. Because the real problem isn’t execution; it’s that there isn’t enough clarity guiding the execution.</p><h3>What good strategy actually does</h3><p>A good strategy doesn’t just point teams in a direction. It changes how work happens.</p><ul><li><strong>Creates focus</strong><br>So not everything becomes a priority, and teams can confidently leave some work undone.</li><li><strong>Forces tradeoffs</strong><br>So decisions don’t feel endless, and discussions move faster.</li><li><strong>Aligns teams</strong><br>So effort builds on itself instead of fragmenting across competing directions.</li><li><strong>Reduces rework</strong><br>Because the “why” is clear before the work begins, not after it starts.</li></ul><p>In practice, a good strategy removes friction from the system. Not by adding more structure, but by creating clarity.</p><h3>A question worth sitting with</h3><p>If any of this feels familiar, you don’t need to overhaul your strategy overnight. Start with one question:</p><p><strong>“What are we intentionally choosing not to do right now?”</strong></p><p>That question surfaces where clarity is missing. It brings tradeoffs into the open and helps teams see whether direction is truly clear or just assumed. As clarity improves, decisions get easier, priorities feel firmer, and work starts to gain momentum.</p><p>If your team feels busy, slow, or stuck in cycles of rework, it may be worth stepping back. Not to add more process, but to ask whether the direction itself is clear enough to guide the work. When strategy isn’t clearly defined, teams compensate with more process, repeated decisions, and extra effort. Until that clarity exists, the work will keep feeling harder than it should.</p><p>If these patterns sound familiar, it can help to step back and take a more structured look at how strategy and alignment are showing up across your team. At <a href="https://livefront.com/">Livefront</a>, we work with organizations to identify where friction is coming from and create clearer direction so teams can move forward with greater focus and confidence.</p><p><strong>Learn more about how we approach this work: </strong><a href="https://livefront.com/see/">https://livefront.com/see/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=23c71d2f972f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/you-might-have-a-strategy-problem-if-23c71d2f972f">You might have a strategy problem if…</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Jurassic Markup: Surviving Custom Email Template Development]]></title>
            <link>https://medium.com/livefront/jurassic-markup-surviving-custom-email-template-development-334797ce71b8?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/334797ce71b8</guid>
            <category><![CDATA[email-development]]></category>
            <category><![CDATA[email]]></category>
            <category><![CDATA[html]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Andrew VanderLeest]]></dc:creator>
            <pubDate>Fri, 15 May 2026 15:24:25 GMT</pubDate>
            <atom:updated>2026-05-15T15:24:24.674Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*g9K7_3pvZ9rGvP7GZa_pow.jpeg" /></figure><p>I was recently tasked with developing a custom email template for an internal company newsletter. When shown the simple design mockup, my brain, still in React-mode, gave my manager an estimate of 2 weeks maximum, alongside my usual client work, to have the template ready for use. A few simple containers holding text and image content– it sounded like Web Development 101. Experienced email devs reading this are either laughing, or wincing in pain. Over the next few weeks I would learn that I had made a disturbingly insufficient time estimate, and incorrectly assumed that any tools and methods from modern web development would be of use to me. Read on and we’ll dig into why email, the technological dinosaur that survived, is one of the most challenging platforms to develop for.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*i0oIwnjx4eMjgHdZq-sxSw.gif" /></figure><p><strong>The Anatomy of An Email, Dissected By Clients</strong></p><p>What I learned in the following weeks was that email developers operate under prehistoric HTML and CSS pretenses and deal with mind-numbing client compatibility woes. Getting a simple single-column, 6-row table of text and images adhering to basic modern design principles to render consistently across a few popular email clients, would indeed require several hundred test emails to myself. I’d repeatedly fix a problem in one client only to find it had unleashed a new one in another. There’s a reason tools like Litmus, which let you mass-preview an email across devices and clients, <em>start</em> their pricing at $500 per month for even the most basic plans. A lot of time, effort, and money goes into developing emails, which isn’t surprising given their still-huge role in marketing and advertising.</p><p>So, what makes the job so challenging? Emails at their core are nothing but text and HTML, split into headers and a body. Even attachments are just base-64 encoded blobs (yes, that’s both ancient and inefficient). Well, each platform and client brings to the table its own filter for what gets through, and developer documentation is often nonexistent across the board. Webmail is rendered by the browser, Apple Mail uses WebKit, Android uses chromium-based components, and Outlook, the biggest beast of them all, uses <em>Microsoft Word’s HTML engine</em> (yes, really) on Windows. Get ready to kiss your divs, flexboxes, and @media queries goodbye. Many clients, including Gmail in certain contexts, aggressively sanitize or strip &lt;style&gt; blocks. I don’t have to explain why JavaScript isn’t allowed in email, but even CSS is heavily limited, as modern CSS can load remote resources, leak data via url(), detect user behavior, fingerprint devices, crash renderers, and execute browser bugs.</p><p>And so the picture becomes clearer…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*0JuEgnBbTnMi8R7oYRrVog.gif" /></figure><p><strong>The Road To Hell Is Paved With Good Intentions: Infinite Backwards Compatibility &amp; Bulletproof Security</strong></p><p>The crux of the matter is, if you want your email to look consistent on every platform and in every client, it needs to use markup and styling that is compatible with <em>literally</em> <em>everything</em>, and leaves no room for security breaches. The site <a href="https://www.caniemail.com/">caniemail</a> is a great tool for checking compatibility of HTML and CSS in emails, and at the conclusion of this article I offer a few do’s and don’ts that will hopefully save some headaches for others new to email development.</p><p>But first, you may wonder, <em>why hasn’t anyone invented a better way?</em> Why was there never an Email 2.0? Why hasn’t the technology grown to better mirror modern web presentation? The answer is two-fold. First, email is federated; nobody owns it. Anyone can run a mail server and communicate via open protocols (like SMTP). As soon as that’s given up, it stops being a universal messaging protocol and becomes a corporate product with terms of service. Email must continue to offer a universal addressability contract. As soon as a message delivery system becomes centralized, it faces data retention laws, content moderation obligations, and jurisdiction problems. Furthermore, it’s hardly the most exciting ecosystem to develop for. Email can’t break old servers, old clients, ancient mail transfer agents, or embedded systems. If a successor to email can’t talk to banks, governments, hospitals, and universities, it’s dead on arrival. Email’s knockout feature isn’t the tech– it’s the unbreakable social contract and Grade-A backwards compatibility.</p><p>Secondly, although undeniably first in importance, is security. In the 90s, spam changed everything, and modern email systems are more hostile-environment filtering engines than they are message delivery protocols. While I still recall the <em>‘You have 7 days to forward this to everyone you know or bad things will happen’</em> emails with a certain humorous, nostalgic fondness (my grandpa forwarded every single one of them to me), spam tactics have since grown far more complex, and weeding out bad actors involves a smorgasbord of systems at work: authentication, sender and domain reputation, user engagement signals, content heuristics, and ML classification. As it’s grown more difficult for bad actors to <em>take</em> your information via email, spam has grown in the latter half of email’s lifetime to employ phishing-centric approaches, requiring the user to <em>give</em> it away. Further discussion on security and spam filtering definitely warrants its own article, but for now consider email clients as not as rendering engines, but security filters that sometimes render HTML.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/390/1*yd8q919czBD9fEV9Cob6eg.gif" /></figure><p><strong>How to Survive Your First Custom Email Adventure</strong></p><p>As I took on the task of developing a custom email template with the goal being any semblance of consistency, my frustration initially grew a mile tall– clearly, it was enough to write an article about it in the aftermath– but in hindsight, I’m left with a respect for a communicative technology that revolutionized the world over half a century ago, has since quietly grown to be nearly bulletproof in the modern day, and remains a staple of communication in daily life. There’s really nothing else quite like it, and it’s a subject I’m glad I explored further. As promised, I’ll leave you with a starter pack of do’s and don’ts for developers on their first journey through email wonderland:</p><p><strong>Do:</strong></p><ul><li>Keep it simple. If you have control over design requirements, a single column, fixed width design not requiring wrapping beyond text will save you many headaches.</li><li>Lean on &lt;table&gt;s for layout. Use role=&quot;presentation&quot;, cellpadding=&quot;0&quot;, cellspacing=&quot;0&quot;, and border=&quot;0&quot; on each of them within your nest to fight back against default table styling in clients.</li><li>Avoid usage of div if possible; it’s supported on paper but has been known to be inconsistent in Outlook.</li><li>Entertain web fonts as progressive enhancement, but don’t lean on them as critical design; always include a system font fallback.</li><li>Slap an !important on any styles you see disappearing between send and receipt. With some luck, the client gremlins that live within will allow them to pass.</li><li>Consider embedded CSS for a tad less development pain over inline styles, but utilize an inlining tool before shipping, as some clients will strip out &lt;style&gt; completely. Platforms like Mailchimp offer built-in inlining tools.</li><li>Test dark mode in clients and operating systems carefully, as some will wildly guess at how to replace colors when turned on.</li><li>Utilize tools as your budget allows. Litmus and Email On Acid allow you to test quickly and broadly, and frameworks like MJML and React Email allow you to build in a manner you’re more familiar with while ultimately boiling down to email client friendly markup and styling.</li></ul><p><strong>Don’t:</strong></p><ul><li>Rely on flexbox, grid, absolute positioning, advanced selectors, or external stylesheets. Support is inconsistent across clients.</li><li>Assume images will load. Many clients will block them by default.</li><li>Embed text in images. This is a sneaky tactic marketing emails use to maintain fancy style, but it’s bad for accessibility. Your email should be readable without images.</li><li>Rely on margin for layout. Margins often collapse or disappear in Outlook. Utilize padding, spacer &lt;td&gt; cells, and explicit height tables.</li></ul><p>Whether these field notes found their way to you out of a cry for help, or you stumbled upon them during a curious venture down a rabbit-hole, I hope they provide you some utility or a dash of perspective into a dinosaur technology that survived to the modern day. For further reading on the wonderful world of web, check out our other articles on <a href="https://livefront.com/insights/all/?type=writing&amp;topic=web-apps">Livefront.com</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=334797ce71b8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/jurassic-markup-surviving-custom-email-template-development-334797ce71b8">Jurassic Markup: Surviving Custom Email Template Development</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Better Tests with Compose Semantics]]></title>
            <link>https://medium.com/livefront/building-better-tests-with-compose-semantics-057a1bdf3437?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/057a1bdf3437</guid>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[semantics]]></category>
            <category><![CDATA[jetpack-compose]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Garrett Schafer]]></dc:creator>
            <pubDate>Fri, 15 May 2026 15:23:57 GMT</pubDate>
            <atom:updated>2026-05-15T15:23:55.573Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AqrbsxHrcWgtjDNxiHRkJQ.png" /></figure><p>If you’ve ever done testing with Compose on Android, you’ve probably figured out that the only testable things are those that appear in the semantics tree. The downside here is that you’re not only limited to the properties that exist in the tree, but also the SemanticMatcher filters that are provided to us that can be found in the Filters.kt file <a href="https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Filters.kt">here</a>. The one thing you can do is use an unmerged semantics tree, but testing things that way doesn’t match how the semantic tree appears to things like TalkBack. We can go further with our Compose testing.</p><h4>Filters</h4><p>The first thing you can do to improve your Composable testing is create additional filters that help find nodes based on existing semantic properties. You’d think there’d be a filter for each of the semantic properties, but I’ve found a lot of useful ones to be missing. One example of this is a filter for the Role property. If you want to find a node with specific Role value, you’d first want to create a filter like so.</p><pre>fun hasRole(<br>  role: Role,<br>): SemanticsMatcher = SemanticsMatcher.expectValue(SemanticsProperties.Role, role)</pre><p>With this filter, can you can find a node with the given role like so.</p><pre>composeTestRule.onNode(hasRole(Role.Button))</pre><h4>Finder</h4><p>We can iterate on this and make this better. Using the filter, we can create a SemanticsNodeInteractionsProvider finder, similar to onNodeWithText or onNodeWithContentDescription. The available finders can be found in the Finders.kt file <a href="https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Finders.kt">here</a>. An example of a finder that uses the hasRole filter would look like so.</p><pre>fun SemanticsNodeInteractionsProvider.onNodeWithRole(<br>  role: Role,<br>  useUnmergedTree: Boolean = false,<br>): SemanticsNodeInteraction = onNode(hasRole(role), useUnmergedTree)</pre><p>This would make it so searching for a node on your test rule would look something like this.</p><pre>composeTestRule.onNodeWithRole(Role.Button)</pre><p>As you can see, this makes the test code a bit cleaner and easier to read. However, what if you have a whole screen of buttons, or a list of buttons, or you’re just testing composables that in this case have multiple of the same role (or whatever semantic property you’re testing for). Then you can use your filter to create an assertion to be used on nodes instead.</p><h4>Assertion</h4><p>Similar to SemanticsNodeInteraction assertions such as assertIsEnabled and assertHasClickAction, you can create your own assertion to verify that a given node has a specific role. An assertion like assertHasRole would look something like this.</p><pre>fun SemanticsNodeInteraction.assertHasRole(<br>  role: Role,<br>): SemanticsNodeInteraction = assert(hasRole(role))</pre><p>which could then be used with your test rule like so</p><pre>@Test<br>fun `swipeable button has button role`() {<br>  composeTestRule<br>    .onNodeWithText(SWIPE_BUTTON_TEXT)<br>    .assertHasRole(Role.Button)<br>}</pre><p>You could see how creating these filters, finders, and assertions can help you test your composables further, or at least easier and cleaner. The Role semantic property was a relatively simple example, but I’ve found a couple others I’ve found useful as well I’d like to share.</p><h4>Progress Bar Indicator</h4><p>If you ever have a progress indicator for a loading screen that doesn’t have a progress value, the way you’d find the node with the given filters would look like so.</p><pre>composeTestRule.onNode(<br>  hasProgressBarRangeInfo(<br>    ProgressBarRangeInfo(<br>      current = 0f, <br>      range = 0f..0f, <br>      steps = 0,<br>    )<br>  )<br>)</pre><p>Not only does this look messy, but I’ve also found that the range info values can be inconsistent and cause tests to fail. In this case we don’t necessarily care about the progress values of the indicator because it’s just a general loading indicator (again, with no progress value). Instead of this approach, I’ve created a filter in the past just to check for any progress bar instead of a progress bar with a specific range info. That filter looks like so</p><pre>fun hasProgressBarIndicator(): SemanticsMatcher =<br>  SemanticsMatcher.keyIsDefined(SemanticsProperties.ProgressBarRangeInfo)</pre><p>Again, you can use this filter to create both a finder and an assertion, but this way you can just check to see if a node has a progress bar indicator or search for a node with any progress bar indicator. I’ve found my loading screen tests to be much more consistent with a filter like this. Your tests would end up looking much cleaner too.</p><pre>composeTestRule<br>  .onNodeWithProgressBarIndicator()<br>  .assertIsDisplayed()<br><br>// or<br><br>composeTestRule<br>  .onNodeWithContentDescription(&quot;Loading&quot;)<br>  .assertHasProgressBarIndicator()</pre><h4>CollectionInfo and CollectionItemInfo</h4><p>Another useful example is when you’re testing items in a list and want to check for collectionItemInfo properties or the correct collectionInfo property values. Creating a filter to check for the correct collectionInfo value is fairly easy and looks something like this</p><pre>fun hasCollectionInfo(<br>  rowCount: Int,<br>  columnCount: Int,<br>): SemanticsMatcher =<br>  SemanticsMatcher(&quot;${SemanticsProperties.CollectionInfo.name} = &#39;$rowCount, $columnCount&#39;&quot;) {<br>    it.config<br>      .getOrElseNullable(SemanticsProperties.CollectionInfo, { null })<br>      ?.let { info -&gt;<br>        info.rowCount == rowCount &amp;&amp;<br>          info.columnCount == columnCount<br>      }<br>      ?: false<br> }</pre><p>Then with the help of a finder, you can write a test to find the node with the correct values to ensure it exists.</p><pre>fun SemanticsNodeInteractionsProvider.onNodeWithCollectionInfo(<br>  rowCount: Int,<br>  columnCount: Int,<br>  useUnmergedTree: Boolean = false,<br>): SemanticsNodeInteraction = onNode(<br>  hasCollectionInfo(rowCount, columnCount), <br>  useUnmergedTree,<br>)</pre><pre>composeTestRule<br>  .onNodeWithCollectionInfo(<br>    rowCount = 1, <br>    columnCount = listOfItems.count(),<br>  )<br>  .assertExists()</pre><p>Then with a filter for the collectionItemInfo</p><pre>fun hasCollectionItemInfo(<br>  rowIndex: Int,<br>  rowSpan: Int,<br>  columnIndex: Int,<br>  columnSpan: Int,<br>): SemanticsMatcher =<br>  SemanticsMatcher(<br>    &quot;${SemanticsProperties.CollectionItemInfo.name} = &#39;$rowIndex, $rowSpan, $columnIndex, $columnSpan&#39;&quot;,<br>  ) {<br>    it<br>      .config<br>      .getOrElseNullable(SemanticsProperties.CollectionItemInfo, { null })<br>      ?.let { info -&gt;<br>        info.rowIndex == rowIndex &amp;&amp;<br>          info.rowSpan == rowSpan &amp;&amp;<br>          info.columnIndex == columnIndex &amp;&amp;<br>          info.columnSpan == columnSpan<br>        }<br>      ?: false<br>  }</pre><p>You can write tests to check that your items all have the correct collectionItemInfo too using an assertion.</p><pre>fun SemanticsNodeInteraction.assertHasCollectionItemInfo(<br>  rowIndex: Int,<br>  rowSpan: Int,<br>  columnIndex: Int,<br>  columnSpan: Int,<br>): SemanticsNodeInteraction = assert(<br>  hasCollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan),<br>)</pre><pre>listOfItems.forEachIndexed { index, item -&gt; <br>  composeTestRule<br>    .onNodeWithText(item.title)<br>    .assertHasCollectionItemInfo(<br>      rowIndex = index,<br>      rowSpan = 1,<br>      columnIndex = 0,<br>      columnSpan = 1,<br>    )<br>}</pre><p>You can see how creating additional filters, finders, and assertions can help you better test things in Compose. What do you do when you want to test things on your composables that don’t have a semantic property? You could slap some testTag modifiers on composables to get them to show up in the semantic tree, but there’s a better way. In my next article, I’ll dive deeper into how we can create our own custom semantic properties and leverage those to further build out our composable tests.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=057a1bdf3437" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/building-better-tests-with-compose-semantics-057a1bdf3437">Building Better Tests with Compose Semantics</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[“Yeah, no”: Using Claude Code to Write Concise PR Descriptions]]></title>
            <link>https://medium.com/livefront/yeah-no-using-claude-code-to-write-concise-pr-descriptions-c5ec2a30b15a?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/c5ec2a30b15a</guid>
            <dc:creator><![CDATA[John Cavalieri]]></dc:creator>
            <pubDate>Thu, 23 Apr 2026 16:43:36 GMT</pubDate>
            <atom:updated>2026-04-23T16:43:34.654Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="A loon pruning a tree with leaves of ones and zeros." src="https://cdn-images-1.medium.com/max/1024/1*SaTryjHMDn6tEPxZRcLPYw.jpeg" /></figure><p>When I moved to Minnesota, I didn’t know much about the place or its culture. All I knew were clichés like the Vikings, Prince, and the Mississippi. I remember asking a new friend if he wanted to play some disc golf with me. I will never forget what he said. He responded, “Yeah, no.”</p><p>That two-word answer captured a local style of brevity that I hadn’t heard before. To those in Minnesota and the Midwest, you already know this is a perfectly logical statement that encompasses the following.</p><ul><li>“Yeah,” meaning “I have received your invitation, and I appreciate that you invited me.”</li><li>“No,” meaning “No thank you.”</li></ul><p>And, this is extra Minnesota nice because it wastes no one’s time with unnecessary words!</p><p>In this article, I will describe how I sometimes use Claude Code to write a Pull Request (PR) description. I’ll share my suggestion for when to use Claude Code in PR descriptions, offer some tips, and share advice on how we can learn from our Midwest friends who want to respect our time. While I use Claude Code, this advice could apply to any AI coding agent.</p><h3>Should you use Claude Code to write your PR Description?</h3><p>The answer to this question typically depends on the size or complexity of the changes. If the changes are small and straightforward, the time to write a prompt is the same as the time to write the PR description. In that case, write the PR description without Claude.</p><h3>Have you set up your ~/claude/CLAUDE.md?</h3><p>Anthropic trained Claude on other people’s code and other people’s writings. You may have a particular style of writing or grammar rules that are important to you that would not come naturally to Claude. Since I’m a stickler for grammar, I have a “Proper Grammar” section in my user memory file located at ~/claude/CLAUDE.md. It includes rules like the following:</p><ol><li>Never drop articles or auxiliary verbs.</li><li>Start sentences with capital letters and end with punctuation.</li><li>Never end sentences with a preposition.</li></ol><p>You may have other grammar rules or just writing styles that are important to you. Claude doesn’t always follow instructions in user memory, but including them still improves your odds.</p><h3>Setting Up Claude For Success</h3><p>The best way for Claude to write a good PR description of your changes is to provide any helpful information.</p><h4>What issue are we solving?</h4><p>Does Claude have access to your issue management system, for example, via the GitHub command-line client or Atlassian’s MCP server? Or is there a separate requirements document? Whatever you have, be prepared to reference or include this information in the prompt. <strong>Including this information allows Claude to explain why we are making the changes</strong>.</p><h3>How to Review Changes with Claude</h3><p><strong>Key Consideration:</strong> Claude’s ability to track your changes depends on whether they fit within the context window. For larger changes, Claude may lose track of earlier edits once the window fills.</p><p><strong>Best Practice:</strong> Avoid relying on prior context. Instead, <strong>ask Claude to reevaluate changes</strong> by referencing a Git command in your prompt.</p><p><strong>Recommended Command:</strong> To review changes, use a git command that fits your scenario, such as this:</p><pre>git --nopager diff main</pre><p>This command shows how your current state differs from the main branch, assuming no prior commits. Adjust the command as needed for your specific scenario.</p><p><strong>Why This Helps:</strong> Including this information helps Claude explain the exact changes you’ve made, rather than relying on context, which may be full of superfluous discussion.</p><h3>Prompting Claude</h3><p>At this point, the essentials are available for Claude: what changed and why. You could ask Claude to write a PR description for you at this point. Claude may see that you have a PR template in your project and use it to draft a PR. Some PR descriptions are too involved for a one‑shot draft. Claude can handle the basics, but design, QA, and security checklists may require manual input beyond what Claude provides.</p><p>I often will start the PR draft in a temp file (e.g. ./tmp/pr.md) where Claude and I can work on it together. I’ll run through and fill in the items I can quickly complete, then leave TODOs for Claude. Then ask Claude to complete the TODOs in the file.</p><pre># Scope<br><br>TODO: Claude, give a concise summary of our changes, especially why we are making these changes.<br><br># Implementation<br><br>- TODO: Claude, concisely list out the changes we made.<br><br># Checklist<br>...</pre><p>Whether you are asking Claude to write a PR description from scratch or to collaborate on one, the most important keywords I have found are these: <strong><em>concise and concisely</em></strong>. By emphasizing “concise,” you encourage Claude to deliver a description that’s both professional and considerate. It’s about respecting your reviewer’s time by cutting out unnecessary verbosity.</p><h4>Give Claude Examples</h4><p>I have also found that Claude is very good at mimicking. So, I keep a few PR descriptions locally to give to Claude as a reference. That can also be helpful if you are doing a series of PRs that may be very similar.</p><h3>Review Claude’s Work</h3><p>It may be tempting to let Claude write the PR description and go with it. Yeah, no. Take a moment and review what Claude wrote.</p><ul><li>Did Claude get it right?</li><li>Did Claude leave anything out?</li><li>Did Claude add information that isn’t relevant to the review, e.g., some implementation detail from the context that makes no sense in the PR description?</li></ul><p>If your experiences are similar to mine, that last case happens more than the others. So, reviewing Claude’s work means editing out irrelevant or excessive details and leaving only the pertinent info needed to help our reviewer.</p><h3>Conclusion</h3><p>Like hearing “Yeah, no” in Minnesota, brevity shows respect. Claude can draft a PR description, but it’s your job to trim it down to what matters. Use AI for speed, but don’t skip your own judgment. Keep the description concise, professional, and useful so reviewers can see exactly what changed and why.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c5ec2a30b15a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/yeah-no-using-claude-code-to-write-concise-pr-descriptions-c5ec2a30b15a">“Yeah, no”: Using Claude Code to Write Concise PR Descriptions</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Your Product Is Your Brand]]></title>
            <link>https://medium.com/livefront/your-product-is-your-brand-60b5da8b6b96?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/60b5da8b6b96</guid>
            <category><![CDATA[design]]></category>
            <category><![CDATA[user-experience]]></category>
            <category><![CDATA[brand-strategy]]></category>
            <category><![CDATA[product-design]]></category>
            <category><![CDATA[product]]></category>
            <dc:creator><![CDATA[Christian Spaulding]]></dc:creator>
            <pubDate>Thu, 09 Apr 2026 16:35:52 GMT</pubDate>
            <atom:updated>2026-04-09T16:35:51.467Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3OsDwWHT9zv6RhlorWBtig.png" /></figure><p>You’ve heard this story before: an ambitious startup unveils a bold new brand identity. Social channels refresh. The website updates. Marketing launches campaigns. Sales updates pitch decks. On the surface, everything looks aligned.</p><p>Behind the scenes, it’s not.</p><p>The brand guidelines are polished but thin: logo usage, colors, typography, and a page on avoiding misuse of the logo. There’s no guidance on how the brand translates into product experiences — no interaction principles, no tone for the interface, no system for real implementation. The document protects the logo but doesn’t enable you to design with clarity.</p><p>As work begins, inconsistencies surface. Marketing feels disjointed. The product is rushed and delivers little value. Reviews turn negative. What was meant to unify creates friction.</p><p>The root issue is simple: the brand is treated as decoration rather than direction. And as a designer, this matters. A product doesn’t just support a brand, it defines it.</p><p>If you ask 100 people what a brand is, you’ll likely hear colors, fonts, ads, tone of voice, and “vibes.” These show a brand, but they don’t tell the full story. At its heart, a brand is the gut feeling people have about a company.</p><p>That feeling isn’t just abstract. Strong brands create trust, and trust changes behavior. When people believe a product will deliver, they’re willing to choose it faster, recommend it to others, and often pay more for it than an identical alternative. Over time, a product that consistently delivers on its brand promise earns the kind of trust that becomes a real competitive advantage.</p><p>A brand is really about how people experience a company’s products and services. It’s the feeling they get after using something you helped design — whether it feels reliable, premium, and thoughtful, or like a total headache. For most companies, that experience happens primarily through a screen.</p><p>People use a product to get a job done. Designers turn to Figma to build quickly; engineers use Claude to code faster; investors choose Robinhood to manage investments easily. All interactions, big or small, shape what users think about a brand. If a product crashes or feels clunky, you’re out. If it’s easy, enjoyable, and solves real problems, trust builds.</p><p>“Can’t marketing just make the product seem better than it is?” Maybe for a short time. But nothing breaks trust faster than overselling and underdelivering. Flashy ads can’t make up for a frustrating experience.</p><p>Here’s what we’ll cover:</p><ul><li>Setting a clear goal</li><li>Drawing usable insights</li><li>Building a system</li><li>Collecting feedback along the way</li></ul><p>Let’s look at how brand strategy could work for a product using a fictional company.</p><p>StartupShip is a project management SaaS tool striving to be the top platform for fast-growing startups. That might sound like a marketing initiative, but it’s really a product decision — one that shows up in the experience you design.</p><p>If StartupShip wants to be known for excellence, that has to come through in the product. We can group the core experience pillars under a single memorable label: Fast, Fluid, Fail-Safe, and Focused.</p><ul><li><strong>Fast:</strong> Performance needs to be quick and reliable.</li><li><strong>Fluid:</strong> The usability and interface elements should feel meaningful and seamless.</li><li><strong>Fail-Safe:</strong> Automation should remove busywork, not create setup headaches.</li><li><strong>Focused:</strong> The experience should help teams prioritize what matters without unnecessary noise.</li></ul><p>Using this checklist clarifies what matters most and helps you evaluate design decisions against the brand promise.</p><p>Now, imagine StartupShip sees that most revenue comes from startups with 10 to 50 employees. Instead of tracking revenue alone, they turn it into a product metric — like increasing weekly active teams in that segment by 40%. Linking revenue goals directly to product usage sharpens focus and informs your interface priorities.</p><p>User insights become actionable:</p><ul><li>An onboarding flow that works for team leads bringing in new members</li><li>Permissions that support growing teams with real complexity</li><li>Starter templates that reflect how startups actually operate</li><li>Project setup without friction</li></ul><p>Now the strategy isn’t just a slide deck — it’s a direction for the product.</p><p>Brand guidelines help align messaging and visuals so ads, landing pages, and the product feel cohesive. Consistency builds recognition.</p><p>But in digital products, brand guidelines alone aren’t enough. They describe the brand, but they don’t guarantee it shows up in the product experience.</p><p>That’s where design systems come in.</p><p>Design systems are how the brand actually lives inside what you build. They turn abstract ideas such as “approachable” or “premium” into typographic scales, color tokens, spacing systems, reusable components, motion patterns, and accessibility decisions.</p><p>Rounded corners and white space can make something feel welcoming. A restrained palette and subtle motion can make it feel refined. These choices shape perception — and perception is exactly what a brand is built on.</p><p>When a design system works well, it does three things:</p><ul><li>Keeps the experience consistent</li><li>Speeds up design and development</li><li>Uses components that have been tested and proven to work</li></ul><p>One mistake is trying to create the perfect system in isolation. Instead, build real features, solve real problems, test them, and then collect the patterns into a system. Let the product lead.</p><p>Start by reviewing your product for inconsistencies. Bring design and product together. Identify what’s working, what’s confusing, and where the brand and product feel misaligned. Document your most-used elements and flows. Map the friction.</p><p>Then pressure-test it:</p><ul><li>Does it feel intentional?</li><li>Is it visually consistent?</li><li>Is it polished enough for daily use?</li><li>Do the flows feel smooth?</li><li>Where does friction show up?</li></ul><p>Then compare that to what the brand promises.</p><p>Because the story at the beginning — the shiny launch with the hollow foundation — doesn’t happen because the logo was wrong. It happens when the product and the brand are disconnected.</p><p>And as a designer, you’re the one who closes that gap.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=60b5da8b6b96" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/your-product-is-your-brand-60b5da8b6b96">Your Product Is Your Brand</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Niche Doesn’t Mean Small]]></title>
            <link>https://medium.com/livefront/niche-doesnt-mean-small-797954f6c40b?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/797954f6c40b</guid>
            <category><![CDATA[product-management]]></category>
            <category><![CDATA[product-strategy]]></category>
            <category><![CDATA[product-design]]></category>
            <category><![CDATA[health]]></category>
            <category><![CDATA[design]]></category>
            <dc:creator><![CDATA[Phillip Johnson]]></dc:creator>
            <pubDate>Thu, 26 Mar 2026 14:41:52 GMT</pubDate>
            <atom:updated>2026-03-30T04:11:39.603Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Upbyw2aHga4XSgWdksuyOw.jpeg" /></figure><h3>Field Notes Chapter Four: AllTrails</h3><p>The world’s preeminent hiking platform, AllTrails, was founded in 2010 as part of the inaugural class of AngelPad, an accelerator that also lays claim to Postmates and Pipedrive. Within two years, AllTrails had approximately two hundred thousand users and by 2018, it had nine million. This figure continued to skyrocket through the pandemic, with “community members” surpassing 30 million in late 2021 and then 90 million in 2025.</p><p>Parallel to that growth was a fair bit of financing. AllTrails raised a seed round of $400k in 2011 and since then has grown both organically and inorganically. In 2016, they acquired EveryTrail from TripAdvisor and in 2019 they acquired iFootpath, <a href="http://trails.com">Trails.com</a>, and GPSies — the latter three made possible due to a 2018 investment of $75M by <a href="https://www.spectrumequity.com/">Spectrum Equity</a>, who remain the majority shareholder of AllTrails despite an additional influx of $150M in 2021 from <a href="https://www.permira.com/">Permira</a>. Finances are private and nearly impossible to determine, but the revenue model has evolved to now include multiple paid membership tiers instead of just a free-with-ads version.</p><p>AllTrails officially exists to “<a href="https://www.alltrails.com/about">help the world find its way outside</a>.” In plain English, they help hikers of all levels find and choose trails anywhere in the world, leveraging their detailed trail maps and a treasure trove of user generated reviews, photos, and ratings. If you’ve gone on a hiking trip or to a national park, you’ve probably used AllTrails.</p><p>This sounds like a pretty simple product. Even if it’s not simple, it’s certainly niche. So how is it so big that a private equity firm could invest $150M in it in 2021 <em>and still not become the majority shareholder</em>? What allowed them to dominate this market? Why haven’t they been disrupted? Could they still improve and widen the gap?</p><p>My colleague Emily gets paid to answer these questions, so here’s where I pass it to her.</p><h3>Perspective from Emily Dziak, Product Designer:</h3><p>AllTrails is a great example of a digital product that picked one thing and decided to do it really, really well. Looking to go for a hike? Just open the app, enter a location, apply a few filters, choose a trail, and hit the road.</p><p>To be fair, you can still find walking and hiking trails in plenty of places besides this app. If you zoom in <em>really</em> far, Google Maps and Apple Maps will show thin white lines indicating paths and trails through wooded areas. You can even watch your little blue dot scoot along as you go. Physical maps have always existed, too. Every national park and roadside state natural area greets hikers with a plexi-covered map placard with color-coded trail lines and <em>hopefully</em> a “You Are Here” sticker slapped somewhere near the parking lot.</p><p>So if you were to ask me, <em>‘What is the m&amp;m in AllTrails’ trail mix that makes it a great product? Why is it preferred over these existing tools?’</em> My short answer would be that AllTrails fills the gap those tools leave in supporting the <em>experience </em>around hiking<em>. </em>For a family on vacation with two grandparents, three kids under ten, and a dog, AllTrails doesn’t just provide tools to find their path, it also gives them confidence that they’ll finish their hike safely, at the expected time, and without any meltdowns or early flights home. The next sections will dive deeper into a few of those tools and why they work so well.</p><h3>Taking community-generated content farther</h3><p>First, users need to find a hike to go on. For a long time, finding a good hike was mostly done through word-of-mouth: friends, guidebooks, or that one coworker who always knows a spot. In addition to building a filtering system that prioritizes hikers’ real-world needs, AllTrails has also replicated the word-of-mouth experience. With user-generated reviews, photos, tips, and notes on trail conditions, finding a hike feels like a conversation with a community knowledge base at scale.</p><p>This scale is what gives AllTrails so much stickiness in the market. Years of accumulated reviews, photos, and new trails create a powerful network effect. Without that “word-of-mouth” credibility hikers rely on, any competitor starts at an immediate disadvantage.</p><h3>Building trust through quality</h3><p>Once users have chosen a hike, AllTrails seamlessly shifts from discovery to navigation. GPS tags open directly in Apple or Google Maps with directions right to the trailhead. Then, once on the trail, the app tracks users’ location along their chosen route in real time, with impressive reliability. That reliability builds confidence and trust in the product.</p><p>AllTrails’ product design reinforces that same sense of confidence throughout the entire experience. As a product, the app fits comfortably alongside benchmark travel products like Airbnb. Products at this level earn immediate trust through thoughtful design and consistent performance. Compared to competitors like CalTopo, AllTrails feels mature and well-tested. Its visual system is modern and minimal, allowing users to focus on the information that matters most when choosing and navigating a hike.</p><p>Because hiking carries real potential risk, this level of polish matters. A beautiful, performant, high-quality experience builds confidence before a user even takes their first step. Knowing you won’t get lost lowers the barrier to getting started, and increases the likelihood you’ll be a return user.</p><p>One notable gap in the AllTrails ecosystem, however, is the web experience. While it aligns visually with the mobile app, it feels like a missed opportunity to support more big-screen activities. An intentional space for deeper, planning-focused tasks, like trip preparation, route comparison, and group coordination, could further strengthen an otherwise excellent product.</p><h3>Growing with users to grow revenue</h3><p>Once a user has completed their first hike, AllTrails shifts from helping users get outside to trying to grow with them, and more importantly, convince them to pay. The problem is that AllTrails already delivers so much value for free. Users can discover hikes, navigate safely, and share their experience back with the community. The core workflow — find, hike, and review — works without spending a dollar. This generosity is great for adoption, but makes upgrading a harder sell.</p><p>AllTrails’ first paid tier, AllTrails+, unlocks a set of advanced safety and navigation features. These features are genuinely helpful, but they would be most valuable to occasional or less experienced hikers — users who might not hike often enough to justify a subscription. This tier asks the least-likely-to-subscribe users to be the first ones to pay. That mismatch points to a structural issue: the app’s biggest fans aren’t the primary targets for this paid tier.</p><p>The introduction of AllTrails Peak, the company’s second paid tier, indicates a much more thoughtful alignment between offering and audience. This tier centers around features for frequent hikers, like community heatmaps that help plan around crowd conditions. Peak features are tools for people who organize their time around hiking, rather than the occasional weekend trip. By focusing on power users, Peak asks the people who use the app the most to be the ones supporting it.</p><p>Just as important as the features themselves is the shift in how they’re introduced. AllTrails’ past strategy relied heavily on bottom sheets and upgrade prompts nudging free users toward a paid plan. Peak feels like the start of a more product-led approach. Users can try features like the Logbook (an in-hike plant and animal identification tool) before a paywall appears. It’s a subtle but meaningful shift. Instead of interrupting the experience to sell value, the product demonstrates value first. For serious hikers, that makes the upgrade feel earned rather than imposed.</p><p>Building on this shift, AllTrails could continue to grow alongside its users. By investing in features that support education and advancement, AllTrails could help casual hikers build their skills, and at the same time move them along the path toward subscription.</p><h3>In Conclusion</h3><p>At its core, AllTrails wins because it reduces uncertainty. It turns “let’s see what happens” into informed confidence. By pairing community knowledge with trustworthy design and increasingly thoughtful monetization, the product supports hikers at every stage of their journey. In a landscape of general-purpose maps and travel tools, AllTrails stands out by staying focused on one simple promise: help people get outside — and make sure they come back.</p><p>If you like reading about product strategy and design, be sure to check out the other installments in our series <a href="https://livefront.com/writing/second-drafts/">here</a>. <strong>If you’re looking for a digital product expert like Emily to help your organization get to the next level, please </strong><a href="https://livefront.com/contact/"><strong>reach out</strong></a><strong> now!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=797954f6c40b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/niche-doesnt-mean-small-797954f6c40b">Niche Doesn’t Mean Small</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agent vs. Assistant: What Makes Agentic AI Fundamentally Different from Generative AI Chatbots]]></title>
            <link>https://medium.com/livefront/agent-vs-assistant-what-makes-agentic-ai-fundamentally-different-from-generative-ai-chatbots-1194271eb061?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/1194271eb061</guid>
            <category><![CDATA[generative-ai-tools]]></category>
            <category><![CDATA[agentic-ai]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Yatharth Garg]]></dc:creator>
            <pubDate>Wed, 25 Feb 2026 18:05:18 GMT</pubDate>
            <atom:updated>2026-03-03T14:04:48.438Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*42gAPWXDC4pqvuxvTlMghg.png" /></figure><p><strong>The Rise of the AI Assistant (and why that was just the start)</strong></p><p>Late 2022 gave us something new: software we could actually <strong>talk to</strong>. Tools like ChatGPT, Claude, Gemini, and Copilot turned search bars into conversations. We used them to draft emails, explain code, summarize meetings, and get unstuck fast. It felt like the internet suddenly grew a friendly brain. These were (and are) <strong>generative AI assistants</strong>: they respond when we ask, and they’re brilliant at producing text, code, and images on demand.</p><p>But assistants have a ceiling - they’re mostly <strong>reactive</strong>. Ask → get output. If you want the output to become an <strong>action</strong> - book the flight, submit the form, run the code and then you still end up doing the work yourself. That’s the gap agentic AI is stepping into.</p><p>OpenAI’s work on <em>Operator</em> and the <em>Computer-Using Agent</em> showed the first big step: an AI that can browse the web like a person by clicking, typing, and finishing tasks in a controlled environment. It’s still early, but it marks a shift from “talks with you” to “<strong>works for you</strong>.”</p><p><strong>From Chatbot to Agent: Same brain, new job</strong></p><p><strong>Agentic AI</strong> isn’t just a chattier chatbot. It’s a system that can <strong>set a plan</strong>, <strong>take multi-step actions</strong>, <strong>look at the results</strong>, and then <strong>adjust </strong>→ all aimed at a goal you care about. Where assistants give you <em>outputs</em>, agents try to deliver <strong>outcomes</strong>.</p><p>Here’s the difference at a glance:</p><pre>[Assistant]<br><br>USER PROMPT -&gt; LLM -&gt; TEXT OUTPUT<br>(great at content, stops at advice)<br><br>[Agent]<br><br>GOAL → PLAN → ACT (tools/APIs/&quot;computer use&quot;) → OBSERVE → REFINE → RESULT<br><br>(browser, code, docs, email, files)<br>(works through steps until &quot;done&quot;)</pre><p>Or, side by side:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/549/1*zFvbDz0Xle81yReSRc7wBg.png" /></figure><p><strong>Real systems you can point to</strong></p><ul><li><strong>OpenAI Operator → ChatGPT Agent.</strong> Operator is a research preview that controls a <strong>virtual computer</strong>: it sees a webpage, types, clicks, scrolls, and completes tasks inside a sandbox. The <strong>Computer-Using Agent</strong> that powers it combines vision and reinforcement learning so the model can operate regular GUIs and not just APIs. In 2025, OpenAI folded this into <strong>ChatGPT Agent</strong> (Agent Mode), so ChatGPT can plan multi-step work, use its “own computer,” and hand you back finished artifacts like spreadsheets or slides.</li><li><strong>Agentic commerce (OpenAI × Stripe).</strong> Another sign of the shift: <strong>Instant Checkout</strong> in ChatGPT uses an <strong>Agentic Commerce Protocol</strong> so agents can complete <strong>real purchases</strong> in a permissioned, auditable way (initially Etsy, expanding to other platforms). It’s a small but important step: from “recommend” to “buy,” with clear guardrails.</li><li><strong>Cognition Labs’ Devin.</strong> Marketed as an “AI software engineer,” <strong>Devin</strong> takes a task, <strong>plans</strong>, writes and <strong>runs</strong> code, reads docs, files issues, and iterates in a sandbox: more like a junior dev who keeps going until tests pass. It’s far from flawless, but it demonstrates the agent pattern in software work: plan → act → check → fix, not just “here’s some code.”</li><li><strong>Replit Agent.</strong> Inside the IDE, <strong>Agent 3</strong> can run for long sessions, test apps in a browser, and auto-repair regressions + a <strong>Plan vs. Build</strong> split so you can review intent before it edits code. It’s a pragmatic example of “assistant” evolving into <strong>autonomous co-worker</strong> for developers.</li></ul><blockquote>A quick cautionary tale: in July 2025, a Replit agent <strong>deleted a live database</strong> during a public test, prompting a CEO apology and new guardrails. Autonomy raises the stakes, which is why sandboxes, approvals, and audit trails matter.</blockquote><p><strong>What actually makes an AI “agentic”?</strong></p><p>Under the hood, most agents still use an LLM as the <strong>brain</strong>. What turns the model into an agent is the <strong>scaffolding</strong> around it:</p><ol><li><strong>Planning &amp; control loop.</strong> Instead of a one-shot answer, the system loops: <em>plan → act → observe → refine</em>. It decomposes goals into steps, picks tools, and keeps going until success or escalation.</li><li><strong>Action surfaces</strong></li></ol><ul><li><strong>Tool/function calls</strong> for reliable APIs (search, calendar, spreadsheets, code execution, payments).</li><li><strong>Computer use</strong> when no API exists, controlling a browser/VM by seeing pixels and sending mouse/keyboard events. That’s what a <strong>Computer-Using Agent</strong> is designed for, and it always runs in a <strong>sandbox</strong>.</li></ul><p>3. <strong>State &amp; memory.</strong> Agents maintain task state (what’s done, what’s next), so they don’t forget intermediate steps — e.g., after gathering sources, they remember to extract data, rank results, and export the final file.</p><p>4. <strong>Safety &amp; policy.</strong> Good agents have <strong>guardrails</strong>: approvals before payments or deletes, least-privilege credentials, and detailed logs of every step.</p><p><strong>Why this shift matters</strong></p><ul><li><strong>We move from outputs to outcomes.</strong> A chatbot lists flights; an agent <strong>books</strong> one (with your OK at payment). That last mile from “what to do” to “done” is where real leverage lives.</li><li><strong>Workflows collapse.</strong> Many knowledge tasks are repeatable pipelines: <strong>gather → transform → file → notify</strong>. Agents can run those end-to-end and hand you the artifact: a spreadsheet, a PR, a summary deck, a form successfully submitted.</li><li><strong>Interfaces simplify.</strong> Products feel lighter at the point of entry. The complexity doesn’t disappear. It moves into supervision, approvals, and trust, which is a better problem than wrestling with the UI itself.</li><li><strong>Teams get a new teammate.</strong> Not one mega-agent for everything, but <strong>small, specialized agents</strong> like a research agent, a spreadsheet agent, a comms agent coordinated by you (or by a supervising agent).</li></ul><p><strong>The challenges (and how people are handling them)</strong></p><ul><li><strong>Safety and containment.</strong> If an assistant hallucinates, you get a bad paragraph. If an <strong>agent</strong> hallucinates, it can submit a form, send an email, or drop a database. That’s why serious systems run agents in <strong>sandboxed VMs</strong>, use <strong>least-privilege</strong> tokens, and insert <strong>approval gates</strong> (e.g., “dry-run checkout” → human OK → charge card).</li><li><strong>Transparency.</strong> Agents should produce a <strong>trace</strong>: what they planned, which tools they called, what came back, and why they made the next move. When something feels off, you need receipts.</li><li><strong>Reliability over long runs.</strong> Multi-step jobs fail for dumb reasons (a button moved, a site timed out). Robust agents recover: they retry, try an alternate path, or ask you for input. Replit’s newer agent versions lean on longer sessions with built-in test/fix loops for exactly this reason.</li><li><strong>Permissions and policy.</strong> Sensitive operations (payments, deletes, production changes) should default to <strong>preview/diff</strong> first, with explicit approval before execution.</li></ul><p><strong>How to apply this thinking (today)</strong></p><ul><li><strong>Start narrow.</strong> Pick one valuable, unambiguous workflow (e.g., “compile weekly metrics into a sheet and email a draft to the team”). Let the agent own that loop.</li><li><strong>Prefer APIs; fall back to computer use.</strong> APIs are safer and more reliable. Use computer-use only when you have to, and keep it in a sandbox.</li><li><strong>Ship with autonomy levels.</strong> <em>Plan</em> (no writes), <em>Edit</em> (local changes), <em>Execute</em> (sandboxed writes with approvals).</li><li><strong>Log everything.</strong> Plans, tool calls, inputs/outputs, screenshots where relevant, artifacts. You’ll thank yourself the first time something goes sideways.</li></ul><p><strong>The road ahead</strong></p><p>The assistant era made software <strong>conversational</strong>. The agent era will make it <strong>collaborative</strong>. We’ll still brainstorm in chat, but more often we’ll hand off <strong>mini-missions</strong> — “research vendors and build the sheet,” “draft and send follow-ups,” “fix this flaky test” and an agent will quietly come back with <em>done</em>, plus a trace you can audit.</p><p>Operator and <strong>ChatGPT Agent</strong> show where this is going. Agentic checkout shows how <strong>transactions</strong> fit in. Devin and Replit show how it plays out in code and across the messy real web. The common thread: <strong>less copy-paste, more completion</strong>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1194271eb061" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/agent-vs-assistant-what-makes-agentic-ai-fundamentally-different-from-generative-ai-chatbots-1194271eb061">Agent vs. Assistant: What Makes Agentic AI Fundamentally Different from Generative AI Chatbots</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Keep Your iOS Tests Quick by Removing Artificial Delays]]></title>
            <link>https://medium.com/livefront/keep-your-ios-tests-quick-by-removing-artificial-delays-532fad61404c?source=rss----75a105744d9a---4</link>
            <guid isPermaLink="false">https://medium.com/p/532fad61404c</guid>
            <dc:creator><![CDATA[Al]]></dc:creator>
            <pubDate>Wed, 25 Feb 2026 16:56:31 GMT</pubDate>
            <atom:updated>2026-02-25T16:56:30.149Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qXD_ZrqPu5UYfzp5MH4wyQ.png" /></figure><p>One of the important things to keep in mind when writing unit tests is to make them as fast as possible. As a project grows, so will the number of tests, which will inevitably make it take longer to run the full test suite. If it takes too long, this will significantly slow down the build-test cycle. This can have several negative effects for developers, including frustration, not running tests, not writing tests, and distraction.</p><p>One of the most pernicious ways test suites slow down is artificial delays getting added to tests. The most obvious form of this is by calling a sleep()<br>function in a test. In a large app I’ve been working on, there are on the order of 5,000 tests. If only 10% of those tests had a small sleep of<br>1 second, it would add a total of 500 seconds (more than 8 minutes) to the tests. Ideally, when doing development work, you should be able to run<br>the full suite of tests after any significant change to verify whether you’ve broken anything. Imagine waiting 8 minutes for that cycle.</p><p>Let’s dive into some reasons why developers add delays to tests, and try to find alternative ways to write the tests to eliminate delays.</p><p>One of the most common places where someone would want to introduce a delay is when waiting for some asynchronous work which calls a<br>completion handler.</p><p>Imagine you have some code like this:</p><pre>class UserGetter {<br>    func getUser(_ completionHandler: (User) -&gt; Void) {<br>        // do some network stuff here…<br>        completionHandler(user)<br>    }<br>}</pre><p>If we try to write a test for this code, we might start with something like this:</p><pre>func test_getUser_callsCompletionHandler() {<br>    let subject = UserGetter()<br><br>    var receivedUser: User?<br>    subject.getUser { user in<br>        receivedUser = user<br>    }<br>    XCTAssertEqual(receivedUser?.name, &quot;John Doe&quot;)<br>}</pre><p>In this test, it will always fail (unless the network is extremely fast), since the assertion runs immediately after getUser(_:) is called.<br>But, if we add a sleep call before the assertion, it will have time to finish loading the user:</p><pre>func test_getUser_callsCompletionHandler() {<br>    let subject = UserGetter()<br>    var receivedUser: User?<br>        subject.getUser { user in<br>        receivedUser = user<br>    }<br> <br>    sleep(1) // Don&#39;t actually do this!<br>    XCTAssertEqual(receivedUser?.name, &quot;John Doe&quot;)<br>}</pre><p>But we shouldn’t do that for some of the reasons mentioned earlier. The sleep adds a fixed delay to the test, whereas we want to try to keep any<br>delays to only what is needed for the asynchronous work to complete. If we add, for example, a sleep for 1 second, the test will always take at<br>least one second to complete, even if the asynchronous work<br>only takes 200 milliseconds, like in this sample execution:</p><figure><img alt="A timeline diagram showing the major elements of the test: a sleep call from 0–1000 ms, the execution of the `getUser()` function from 0–200 ms, and the test verifying results at 1000 ms." src="https://cdn-images-1.medium.com/max/1024/1*n8xDkxjEFvxfeO_5DjywOQ.png" /></figure><p>The goal is for the test to immediately resume execution once the asynchronous work is done. For this, we can use XCTest’s built in support for expectations.</p><p>The XCTestExpectation class provides a way for a test to wait for a signal from asynchronous operations, indicating they have completed. The<br>general pattern of use is as follows:<br>- The test creates an expectation by calling XCTestCase.expectation(description:), passing a description for the expectation<br>- The test starts some asynchronous work which calls XCTestExpectation.fulfill() when it completes.<br>- The test calls XCTestCase.wait(for:) to wait for the expectation to be fulfilled.</p><p>Here’s what our test looks like when we add the expectation:</p><pre>func test_getUser_callsCompletionHandler() {<br>    let subject = UserGetter()<br>    var receivedUser: User?<br>    let expectation = expectation(description: &quot;Waiting for completion handler&quot;)<br>    subject.getUser { user in<br>        receivedUser = user<br>        expectation.fulfill()<br>    }<br>    wait(for: [expectation])<br>    XCTAssertEqual(receivedUser?.name, &quot;John Doe&quot;)<br>}</pre><p>Now, the test will start the getUser() operation and wait for the expectation before asserting the user is not nil. The wait(for:) call will<br>immediately return as soon as the completion handler calls expectation.fulfill(), so the test does not spend any time waiting that isn’t absolutely necessary. We end up with an execution like this:</p><figure><img alt="A timeline diagram showing the major elements of the test: a wait call from 0–200 ms, the execution of the `getUser()` function from 0–200 ms, and the test verifying results at 200 ms." src="https://cdn-images-1.medium.com/max/1024/1*X3OZh027HZx9tMI7sNHrJw.png" /></figure><p>You can see that the execution is much tighter, and there isn’t wasted time like the example with the sleep().</p><p>This is one of the more ideal scenarios where an expectation can be used to avoid sleeping in the test. In the examples we took the execution time from 1.2 seconds to 0.2 seconds, which is a significant improvement. Realistically, in most tests you should be able to do much better than 200 milliseconds, and you want to get as close to zero as possible, so you can get quick feedback as your project grows.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=532fad61404c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/livefront/keep-your-ios-tests-quick-by-removing-artificial-delays-532fad61404c">Keep Your iOS Tests Quick by Removing Artificial Delays</a> was originally published in <a href="https://medium.com/livefront">Livefront</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>