<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Andi Smith - Blog</title>
    <link>https://www.andismith.com/blogs</link>
    <description>Technical Leader, Product Engineer and AI Consultant in London, UK</description>
    <language>en-us</language>
    <lastBuildDate>Wed, 15 Apr 2026 22:47:59 GMT</lastBuildDate>
    <atom:link href="https://www.andismith.com/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>10 Things I Learned From CTO Craft Con 2026</title>
      <link>https://www.andismith.com/blogs/2026/03/10-things-i-learned-from-cto-craft-con-2026</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2026/03/10-things-i-learned-from-cto-craft-con-2026</guid>
      <description>My highlights from the two day London conference.</description>
      <content:encoded><![CDATA[<p><strong>My notes from an excellent CTO Craft Con 2026.</strong></p>
<p><a href="https://conference.ctocraft.com/london-2026/">CTO Craft Con</a> has been running for 9 years, but this year was my first chance to attend — and I'm pleased to say it didn't disappoint. As you'd expect given today's technical landscape, there was a lot of focus on AI and how it's been reshaping teams, particularly since December 2025.</p>
<div class="post-image">
<img alt="Conference" style="margin: 0 auto; max-width: 90%" src="https://www.andismith.com/assets/blogs/2026/03/10-things-i-learned-from-cto-craft-con-2026/cto-craft-1.jpg"><br>
Small changes. Real impact.
</div>
<h2>Highlights / Thoughts</h2>
<h3>AI is Everywhere</h3>
<p>AI is everywhere, and unsurprisingly it dominated the conversation in and out of the auditorium. I was keen to get a real sense of where people are in this journey.</p>
<p>I think everyone agreed it was disrupting common practice. There was no shortage of enthusiasm about what AI should be able to do - and while specific agentic AI success stories are still emerging, the direction of travel is clear. The industry is still in discovery mode. But the overall message was simple - pay attention, or risk getting left behind.</p>
<h3>What Makes a Good Hire?</h3>
<p>With the push for AI, what makes a good hire has changed. Several talks touched on the need for developers to also be good product people - and this came up repeatedly in conversations between sessions too. AI is merging the roles of product, engineering, design and UX together.</p>
<p>Over the last few months, my hiring focus has shifted towards people who can not only code well but also bring a product mindset and entrepreneurial outlook. For those who can do two of the three reasonably well, this is a genuine land of opportunity.</p>
<h3>Empathy was a running theme</h3>
<p>Several speakers raised empathy as the key differential between humans and AI. In particular, they commented that they felt empathy would be something that AI would never be able to replicate as it requires human effort.</p>
<p>The time you invest in your teams is part of the reason why empathy feels genuine; and even if AI could simulate it, it wouldn't carry the same weight.</p>
<p>As roles evolve in this new era, showing up with empathy for your team through that change matters more than ever.</p>
<div class="post-image">
<img alt="Conference" style="margin: 0 auto; max-width: 90%" src="https://www.andismith.com/assets/blogs/2026/03/10-things-i-learned-from-cto-craft-con-2026/cto-craft-2.jpg"><br>
When a model performs better but at the cost of being more verbose
</div>
<h3>AI as a Thinking Partner</h3>
<p>We've become accustomed to AI completing tasks for us in product engineering but <a href="https://www.linkedin.com/in/jstanier/">James Stanier</a> showed us how he was using it to reduce leadership isolation, manage his ideas and notes via a scratchpad of ideas and automate the mundane, like gathering metrics.</p>
<p>To borrow wisdom, James shares a list of transcribed podcasts, interviews and blog posts from people he admires, has AI summarises them and then includes those insights in his context files.</p>
<p>I'll certainly be playing around further with the idea of an AI Executive Assistant with all these different agent roles to help me work.</p>
<h3>Moving the Bottleneck</h3>
<p><a href="https://www.linkedin.com/in/willlytle/">Will Lytle</a> spoke about how AI has made writing code cheap, but it has just moved the bottleneck further down the Software Development Life Cycle. He referred to a quote from State of DevOps 2025:</p>
<blockquote>
<p>AI doesn't create organisational excellence - it amplifies what already exists. The value of AI will be unlocked by re-imagining the system of work it enables.</p>
</blockquote>
<p>He spoke about the RACER framework which I'll be experimenting with over the next few weeks:</p>
<ul>
<li>Rollout: deploy and use the tools</li>
<li>Approach: align the right tools with the right use cases</li>
<li>Constraints: systematically eliminate barriers</li>
<li>Engineering Impact: measure impact on productivity</li>
<li>Results: achieve business outcomes</li>
</ul>
<p>If you're navigating AI bottlenecks in your engineering org, <a href="https://plandek.com/resources/the-framework-for-ai-augmented-engineering-racer/">his slides are sure to help</a></p>
<h3>From SaaS to Agentic Platforms</h3>
<p><a href="https://www.linkedin.com/in/jaime-de-mora-424a831a/">Jaime de Mora</a> made a compelling case for how SaaS is being disrupted by agentic AI.</p>
<p>He argued that interoperability needs to be the foundation of agentic systems — built on MCP protocols and CLIs — and showed how agentic products are increasingly being measured, and priced, by outcomes rather than usage. Intercom's Fin is a great example where they charge per successful ticket resolution, not per seat.</p>
<p>The market is shifting from traditional SaaS metrics like DAU, MAU, time in app and feature usage to outcome-based measures: tasks completed, time to resolution, cost per task, and ROI achieved.</p>
<h3>Owning the Stack</h3>
<p>Moving away from AI, <a href="https://www.linkedin.com/in/sathyanandakumar/">Sathya Nandakumar</a> delivered one of the most compelling talks of the conference - covering Holland &#x26; Barrett's complex journey from legacy systems to in-house platforms.</p>
<p>She spoke candidly about staying committed to the goal through significant challenges, and shared hard-won lessons on the importance of communicating complex migration work to stakeholders.</p>
<h3>Leading From the Edge</h3>
<p>Similarly, <a href="https://www.linkedin.com/in/dr-ilknur-colak-5305225a/">Dr. Ilknur Colak</a> drew compelling parallels between climbing the Seven Summits and technical leadership.</p>
<p>There were some great tips for leaders in here such as building resilience before the storm, taking ownership with others hesitate, using bold communication to build trust and staying calm in uncertainty.</p>
<p>And beyond the leadership lessons, her personal story of conquering some of the world's most demanding terrain was genuinely inspiring.</p>
<h3>Glue Work</h3>
<p>Somehow I hadn't come across the term "glue work" before, but I quickly recognised it as something that I had come across many times over the years within my own leadership skills and team.</p>
<p>In her talk, <a href="https://www.linkedin.com/in/mseckington/">Melinda Seckington</a> presented how to share glue work through the team and through the community - and how sharing that glue work builds leadership skills and shapes engineering culture.</p>
<p>Melinda propsed six levels to share the glue work between teams:</p>
<p><strong>Perpertual Exposure</strong></p>
<ol>
<li>Recognise and Celebrate</li>
<li>Build Shared Knowledge</li>
<li>Connect People</li>
</ol>
<p><strong>Deliberate Practice</strong></p>
<ol start="4">
<li>Learn By Doing</li>
<li>Learn By Teaching</li>
<li>Learn By Leading</li>
</ol>
<h3>The Human Load Balancer</h3>
<p>Another talk that hit home was <a href="https://www.linkedin.com/in/becca-anderton/">Rebecca Anderton</a>'s session about the human load balancer and recognising when shielding your team actually becomes a burnout risk to you as a leader.</p>
<p>She spoke about the invisible work that leaders often do to protect their team - which works in the short term but creates problems further down the line.</p>
<p>So instead Rebecca advised on how to step away from being always-on and build sustainable leadership practices such as sharing context, building boundaries with intent and sharing the load on purpose that in the long term protect both the team and leader.</p>
<div class="post-image">
<img alt="Conference" style="margin: 0 auto; max-width: 90%" src="https://www.andismith.com/assets/blogs/2026/03/10-things-i-learned-from-cto-craft-con-2026/cto-craft-3.jpg"><br>
There was also lots of advice on hiring and people management
</div>
<h3>Conclusion</h3>
<p>There were so many great talks and insights that I haven't mentioned here, and I genuinely found something of value in almost every one of the talks.</p>
<p>Looking forward to the next CTO Craft Con — maybe by then AI will have advanced enough to take the stage itself!</p>]]></content:encoded>
      <pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Undefined Roles</title>
      <link>https://www.andismith.com/blogs/2026/03/undefined-roles</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2026/03/undefined-roles</guid>
      <description>With the acceleration of AI, the traditional boundaries between product, engineering and UX are collapsing.</description>
      <content:encoded><![CDATA[<p><strong>With the acceleration of AI, the traditional boundaries between product, engineering and UX are collapsing.</strong></p>
<p>For as long as I can remember, in most businesses these disciplines have operated in clearly defined lanes. Product owned the vision, engineering built it, and UX made it usable. Each role had its specialists, its processes, and its seat at the table. In the start-up world, the lines have always been a bit more blurred with team responsibilities, but with this new wave of agentic AI even larger teams are going to find that this model is under pressure.</p>
<p>AI is compressing the skill gap in ways that would have seemed unlikely even six months ago. The cost of writing code is lower than ever. Interfaces can be prototyped quickly, thrown away and restarted with almost no penalty. Product decisions can be validated without a full team behind them. The bottleneck is no longer technical execution. Instead, the bottleneck is thinking time.</p>
<p>This is already playing out. Engineers are now making product calls. Designers are prototyping in code. Product managers are shipping without engineering dependency. The lines aren't just blurring — they're genuinely dissolving. A single person can now explore an idea from concept to working prototype in a single day.</p>
<p>This has real implications for how we build and hire. The most valuable people aren't necessarily deep specialists anymore. They're people who can move fluidly across disciplines, hold the bigger picture in mind, and make good decisions with incomplete information. T-shaped skills are giving way to something harder to define but increasingly recognisable when you see it. None of this means specialisation is dead. But the weight is shifting.</p>
<p>So if you are working in product engineering, what should you do?</p>
<p>The best place to start is by getting curious about the disciplines adjacent to yours. If you're an engineer, spend time understanding why product decisions get made. If you're in product, get closer to the technical constraints. If you're in UX, understand the business outcomes you're designing for.</p>
<p>You don't need to become an expert in everything — but developing fluency across the stack, both technical and human, is increasingly the differentiator. The people who will thrive in this new world are those who lean in rather than wait to see how it plays out.</p>
<p>Change is coming whether we're ready or not. The right response isn't anxiety. It's curiosity. Bring a product mindset, an entrepreneurial spirit, and a genuine openness to working differently.</p>
<p>Nobody has this fully figured out yet. It's a work in progress - for all of us. The real advantage right now is curiosity.</p>]]></content:encoded>
      <pubDate>Sat, 07 Mar 2026 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>AI Is Only As Good As Your Understanding</title>
      <link>https://www.andismith.com/blogs/2026/02/ai-is-only-as-good-as-your-understanding</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2026/02/ai-is-only-as-good-as-your-understanding</guid>
      <description>If AI is so good at solving problems, why do you still need employees?</description>
      <content:encoded><![CDATA[<p><strong>If AI is so good at solving problems, why do you still need employees?</strong></p>
<p>Reading that back, that sounds brutal. But as time goes on and the models improve, it's a reasonable question. The tools are genuinely impressive. But the question misidentifies the bottleneck. Product development isn't slow because writing code is hard. It's slow because understanding what to build and why is hard. AI works to requirements. It doesn't work to intent.</p>
<h2>Requirements vs Intent</h2>
<p>Imagine asking your AI tooling to help simplify an onboarding flow.</p>
<div align="center">
<strong>Shorter onboarding = less friction = better conversion</strong><br><br>
</div>
<p>This looks like a sensible goal. The AI can see exactly what you were optimising for, so it recommends removing a step that on the surface looks like it is adding unnecessary complexity.</p>
<p>The problem? That step isn't complexity. It's the core business proposition. Remove it and you're not building a simpler product. Best case you've lost some context of what you were trying to build. Worst case you're building a different product entirely.</p>
<p>You could argue that you should have given the AI better context. And you'd be right. But that's precisely the point. The context has to come from somewhere. It comes from months of customer conversations, failed experiments, and hard-won decisions that sit in the heads of the people who built the business. You can't shortcut to it. You have to earn it.</p>
<p>Requirements are what you ask for. Intent is why you're asking for it, and what you're ultimately trying to achieve. The difference between the two is where products win or lose. The AI could see the requirement was to simplify onboarding but had no way of knowing the intent behind the step it recommended removing.</p>
<p>The tools don't compress the learning that builds that intent. They don't replace the iteration. They don't skip the moment where you realise you built the wrong thing and have to go back. Customer feedback, market signals, the hard conversations with users - these are still what separates a product with a point of view from one that just functions.</p>
<p>What AI changes is how quickly you can execute once that intent is clear. That's genuinely valuable. But the intent still has to be defined first, and the only way to build it is to go through the process.</p>
<h2>What This Means For Your Business</h2>
<p>AI is very good at the parts of product development that are routine and repeatable. It's the unique parts — your business model, your customers, the decisions that make your product yours — where it falls short. That's not a temporary limitation waiting to be solved by the next model release. It's the nature of what these tools are.</p>
<p>As building code gets faster and cheaper, the constraint shifts. It's no longer about how quickly you can produce something. It's about how clearly you can decide what to build.</p>
<p>The businesses that succeed in this AI era won't be the ones who generate the most output. They'll be the ones who remain clear about what makes them valuable — and use AI to build something that reflects that, rather than something that just looks like everything else.</p>
<p>That's a higher bar than it used to be, not a lower one. Speed is easy now. Knowing what to build is the hard part.</p>]]></content:encoded>
      <pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Lean Coffee Decision Making</title>
      <link>https://www.andismith.com/blogs/2026/02/lean-coffee-decision-making</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2026/02/lean-coffee-decision-making</guid>
      <description>A way to come to decisions fast, when time is not on your side.</description>
      <content:encoded><![CDATA[<p><strong>A way to come to decisions fast, when time is not on your side.</strong></p>
<p>Sometimes time is not on your side. Your team is busy building features, but the discussions around how we build and with what are building up.</p>
<p>My CTO coaching mentor, Joel Chippindale, once introduced me to a technique called <a href="https://agilecoffee.com/leancoffee/">lean coffee</a> - a structured but agenda-less meeting. For the CTO circle I was participating in it worked incredibly well for ensuring we each got to speak about the topics that were important to us and that we were invested in what others had to say.</p>
<p>So I decided to implement a variant of it within my teams.</p>
<h2>What is Lean Coffee?</h2>
<p>With lean coffee participants gather, build an agenda together, and begin talking. After five minutes, the organiser interrupts the group and asks everyone to vote thumbs up/down on whether to continue discussing or move to the next priority topic. If majority votes to continue, you get another shorter time duration.</p>
<h2>How we use Lean Coffee</h2>
<p>To be the most time efficient for our team I've been trialling a slight variation on lean coffee which works as follows:</p>
<ul>
<li>We schedule a regular team meeting</li>
<li>24 hours before the session, the organiser asks participants to propose suggestions on what they wish to discuss on a Slack thread</li>
<li>Participants are also asked to place emojis next to those they feel strongly about</li>
<li>Shortly before the session, the organiser orders the topics by number of emojis</li>
<li>The session begins and follows the usual lean coffee structure:
<ul>
<li>Each topic gets a timebox (5 minutes)</li>
<li>If the topic doesn't naturally conclude before 5 mins, we vote thumbs up/thumbs down on whether to continue</li>
</ul>
</li>
<li>The aim is to have an action/resolution to each item</li>
</ul>
<h2>The Results</h2>
<p>So far it has been working incredibly well for us. It has helped us make a lot of rapid decisions on the way we work without requiring a lot of time from the team.</p>
<p>Lean Coffee turns control back over to the attendees of the meeting; it reduces the ability for one person to dominate the conversation and it ensures the team are able to talk about the topics that matter to them - which means their ownership, accountability and engagement has also improved.</p>
<p>Give lean coffee a try!</p>]]></content:encoded>
      <pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Relationship Radar</title>
      <link>https://www.andismith.com/blogs/2026/01/relationship-radar</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2026/01/relationship-radar</guid>
      <description>The people you&apos;re closest to at work aren&apos;t always the people you need to be closest to.</description>
      <content:encoded><![CDATA[<p><em>The people you're closest to at work aren't always the people you need to be closest to.</em></p>
<p>As engineering managers, we can spend most of our time deep in execution. We spend our day to day unblocking teams, managing delivery and navigating difficult technical decisions. But influence in an organisation doesn't flow through org charts. It flows through relationships. And most of us have never deliberately audited ours.</p>
<h2>Mapping Where You Are</h2>
<p>One exercise I've found genuinely useful is the <strong>Relationship Radar</strong>.</p>
<p>The concept is simple. Draw a circle. Place yourself at the centre. Now plot the people you should be working with on the circle - not by title or reporting line, but by proximity. How close is your actual working relationship with each person?</p>
<div class="post-image">
<img style="max-width: 400px;" alt="An example of a relationship radar" src="https://www.andismith.com/assets/blogs/2026/01/relationship-radar/radar.jpg"><br>
An example of a relationship radar
</div>
<p>The exercise only works if you're honest with yourself about your relationships when you complete your circle. For example, the QA lead you sync with daily will be close. The CFO you've met twice sits near the edge.</p>
<p>What you'll likely find is a cluster of familiar faces near the middle — and a ring of people on the outside who probably shouldn't be there.</p>
<h2>The Gap Is the Point</h2>
<p>The radar's real value isn't the map itself. It's the gap between where people <em>are</em> and where they <em>should</em> be.</p>
<p>Who are the stakeholders whose priorities you don't fully understand? Whose buy-in you'd need if you wanted to push through a meaningful change? Whose opinion shapes decisions before they ever reach you?</p>
<p>Those are the relationships worth investing in - and you should invest in them before you need something from them.</p>
<h2>Proximity Is a Choice</h2>
<p>Strong engineering leaders don't wait for relationships to form organically. They build them deliberately. Go for a regular coffee, ask a genuine question about someone's priorities and show curiosity about problems outside your immediate domain.</p>
<p>The radar won't tell you what to do. But it will tell you where to start and <a href="/blogs/2024/11/lets-talk-about-1-1s">who you should be booking in a 1-1 with</a>.</p>]]></content:encoded>
      <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Where to Start?</title>
      <link>https://www.andismith.com/blogs/2025/12/where-to-start</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/12/where-to-start</guid>
      <description>Opus 4.5 is out and it feels like it&apos;s a big step forward in AI innovation.</description>
      <content:encoded><![CDATA[<p><strong>Opus 4.5 is out and it feels like it's a big step forward in AI innovation.</strong></p>
<p>The new model outperforms other models when working on long-context tasks and more complex reasoning chains. It's great for long coding tasks and multi-file refactors, but it's also great for creating working solutions for other parts of the business too.</p>
<p>Maybe you're not convinced and still on the other side of the fence when it comes to AI. Regardless of what you think of the model benchmarks, the tools are good enough now that waiting to invest in AI is the riskier bet.</p>
<h2>Getting Started</h2>
<p>My team has been experimenting with AI a lot recently, and we're starting to see some great results. But if you're still new to the AI scene, and confused where to start it can be difficult to know what to do.</p>
<h3>1. Get a Subscription!</h3>
<p>Give your team Claude Code subscriptions to get them going!</p>
<p>Ensure they have a safe place to start exploring how to use the tool and play with how it works. Some engineers feel threatened by AI, whilst others over-trust the output. You can acknowledge this by framing Claude Code as a "junior pair programmer that needs review".</p>
<p>Avoid starting with a production-critical feature and instead begin experimenting on internal tools or fixing bugs instead.</p>
<p>$200/month for a license for developers will quickly pay itself back when they get used to the tools.</p>
<h3>2. Knowledge Sharing</h3>
<p>Give developers a way to share their learnings with one another, and start to build a shared context domain with the things that work within your repo. This could be through a shared Slack channel or regular catch ups. Encourage the team to talk about AI.</p>
<h3>3. Guardrails</h3>
<p>All the old rules to software development still apply.</p>
<p>Guardrails are incredibly important. The AI will inevitably go on tangents and will still write bloated code from time to time. AI isn't an excuse to start throwing out the rules of good code and PR hygiene.</p>
<p>Code should follow standard patterns, have explicit naming and organisation conventions, and a clear separation of concerns.
PRs should still follow best practice rules. Keep code changes small. Keep writing tests. Keep reviewing code.</p>
<p>Tests aren't just a safety net, they're the feedback loop that keeps the agent honest. <a href="/blogs/2025/03/vibe-coding">Consider utilising TDD to get the AI to write code to pass the tests</a>.</p>
<p>Creating an AI bot (or using a tool like CodeRabbit) with your Github pull requests to check the PR against the standards will give you an extra line of defence, but human validation is still incredibly important.</p>
<h3>4. Standards</h3>
<p>Keep documenting your coding standards.</p>
<p>Create a standards document within your code repository that your team and your AI works from - covering things like database patterns, coding principles, security requirements. Better documentation of your code base will lead to more predictable results from your AI agents.</p>
<p>Full-stack ownership starts to become more natural with AI, you may find engineers moving out of their front end or back end specialities to work more efficiently; so it's important they (and their AI) can understand how to do that with this shared foundation.</p>
<p>Grow this document over time from the things that frustrate you.</p>
<h3>5. Build Agent Skills</h3>
<p>Once your team has the basics down, you can start building reusable skills for common tasks. For example, scaffolding a new service, writing tests for existing code, generating PR descriptions, or setting up a new feature end-to-end.</p>
<p>These are essentially prompt templates or task definitions that live in your repo alongside your standards document. They're how the compounding gains start to show up.</p>
<p>Make sure you are saving shared agent context with your PRs, so instead of every developer figuring out how to prompt the agent for a database migration, someone can write it once and everyone will benefit.</p>
<h2>Conclusion</h2>
<p>The cost of experimenting is low, the iteration cycles are fast, and the most expensive thing you can do right now is stall. Teams that are seeing 10x productivity gains didn't plan their way there — they started small and built confidence. The best time to start with AI was several months ago, the second best time is now.</p>]]></content:encoded>
      <pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>All Roads Lead to React Native</title>
      <link>https://www.andismith.com/blogs/2025/09/all-roads-lead-to-react-native</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/09/all-roads-lead-to-react-native</guid>
      <description>For a startup, choosing the wrong app technology can cost you millions in burn and years of wasted engineering time.</description>
      <content:encoded><![CDATA[<p><strong>For a startup, choosing the wrong app technology can cost you millions in burn and years of wasted engineering time.</strong></p>
<p>Unless you see your business as a web only platform, there's a good chance you're going to need to make a choice early on about the technology used to build your app. This is a choice that will more than likely live as long as the company itself and will define your engineering team structure, development speed and budget for years to come. So it's important to get it right.</p>
<p>I've spent much of my career working with different companies and trying the latest and greatest technologies. Technology changes at a rapid pace, and often trends come and go quickly. But every so often you get something that sticks around.</p>
<p>React is a good example of one such technology. Before React, JavaScript frameworks felt like they were on rotation week by week with Angular, Dojo, MooTools, Ember, Knockout, Backbone, Meteor, Sencha and others being introduced and sometimes forgotten at rapid pace. Choices are still available in the web framework space - Vue.js and Svelte, for example - but with over 10 million developers worldwide using React, it's hard to ignore.</p>
<p>For a start up, there is often a business need to have a mobile app too. If you didn't want to commit to native, there's been a variety of frameworks that have come and gone in this space too - some which were incredibly difficult to work with or didn't feel like a native experience at all. Cordova, Ionic, Sencha Touch and jQuery Mobile spring to mind.</p>
<p>Today, the only real cross-platform competitors with staying power are React Native and Flutter, but their philosophies and ecosystems differ in ways that matter for startups.</p>
<p>React Native originally felt like a gateway for React developers to try mobile development, but lacked a lot of the tools and processes to make that journey easy. Like the frameworks I mentioned before, it also didn't feel very native - and as soon as you needed to build something outside of content you were hit with writing Swift or Kotlin anyway.</p>
<p>Over the last two years, the React Native and Expo teams have really pushed forward React Native as a viable technology for mobile apps and as of 2024, both Stack Overflow and Github stars show React Native is consistently among the top mobile frameworks, ahead of Flutter. Let's take a look at why.</p>
<h2>Support</h2>
<p>Improvements in what we can build in React Native have never been as rapid as they are now.</p>
<p>In 2024, the React Native team announced the New Architecture, which combined support for modern React features with direct access to native interfaces on mobile. This change has made apps feel far closer to the experience you'd expect from a Swift iOS or Kotlin Android application. The performance concerns that plagued early React Native applications are largely resolved with the New Architecture, with apps now achieving near-native performance while maintaining cross-platform benefits.</p>
<p>The Expo team themselves have been busy building a huge amount of libraries to make React Native development easier - from audio and video; to sensors and haptics; and security and storage functionality like biometrics and SQLite integrations.</p>
<h2>One Codebase, Multiple Platforms</h2>
<p>React Native allows developers to build apps with one codebase and release to multiple platforms - commonly iOS and Android, with Web support also possible with some extra effort. For cash-strapped startups, this resource efficiency is React Native's biggest advantage. Instead of hiring separate iOS and Android teams, you can build one team of JavaScript developers who can ship to both platforms.</p>
<p>But this isn't only about saving money - it's about speed. When you need to iterate quickly based on user feedback, having one codebase means one set of changes, instead of coordinating updates across multiple teams. However, a single codebase doesn't mean development time is completely halved for iOS and Android - each phone operating system has its own quirks - but it does mean that it is quicker to build in a single codebase and you are assured of feature parity between the two platforms. And if your idea pivots, then you only have one codebase to change.</p>
<h2>Talent Accessibility</h2>
<p>Like React, the talent pool for React Native developers is only growing. What was once a single developer adventure into app prototyping is now becoming a technology to seriously build full teams around. The concepts taken from React mean the doors are open to millions of developers to learn React Native quickly and efficiently.</p>
<p>Due to Flutter's different approach in this area, rendering everything through its own graphics engine, developers need to know how to program in Dart - a language with far fewer developers than JavaScript, making hiring more challenging and expensive for startups.</p>
<h2>The Bottom Line</h2>
<p>If your startup needs to prove product-market fit, React Native helps you move twice as fast with almost half the engineering spend. Companies like Discord, Shopify, Coinbase and Microsoft are big supporters of React Native citing development and iteration speeds as their main gains.</p>
<p>It's worth being clear: React Native is not a silver bullet. If your product is a 3D game or relies heavily on AR/VR then native code or specialized engines like Unity are still the best choice. But for the vast majority of startup apps - in the commerce, productivity, social, media business spaces - React Native provides near-native performance with far greater efficiency.</p>
<p>Flutter is strong, but React Native's ability to leverage existing React talent, its proven ecosystem, and Expo's investment make it the pragmatic choice for most startups.</p>]]></content:encoded>
      <pubDate>Sun, 14 Sep 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>All the Small Things</title>
      <link>https://www.andismith.com/blogs/2025/08/all-the-small-things</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/08/all-the-small-things</guid>
      <description>In a startup it&apos;s important to reduce friction. The main asset available to you is to go fast. If you can&apos;t do that, you won&apos;t beat the competition.</description>
      <content:encoded><![CDATA[<p><strong>In a startup it's important to reduce friction. The main asset available to you is to go fast. If you can't do that, you won't beat the competition.</strong></p>
<p>It's often the small things that create the most friction. Minor issues compound in to major problems. This can be due to frequency multiplication, context switching penalties or motivation drain.</p>
<p>Here are some of the common areas I see friction in startups:</p>
<ul>
<li>
<p>Approval processes with either unnecessary steps or unclear criteria. For example, purchasing a license to a critical piece of software.</p>
</li>
<li>
<p>Unclear decision making authority. When it's unclear who is the person who can actually say yes to a decision. Does it always have to be the founder or CEO, or can they hand off some decisions to other people?</p>
</li>
<li>
<p>When requirements aren't locked down. When there's uncertainty within the team on what is actually being built and a real danger of scope creep beyond what is really important.</p>
</li>
<li>
<p>Documentation that is hard to find. When it isn't clear which tool is the right place to document, processes get documented in different places or nowhere at all. They get lost.</p>
</li>
<li>
<p>Information overload without clear prioritisation creates cognitive overload. When every task is shouting for attention at once it's hard for the team to understand what is important.</p>
</li>
<li>
<p>Pull requests that take days to review. Either due to the right people being unavailable, or due to the size of the code change, or a lack of coding style guidelines.</p>
</li>
</ul>
<h2>Looking for Friction</h2>
<p>You can spot the signs of friction within an organisation by looking for these signals:</p>
<ul>
<li>Tasks that people avoid or continuously delay</li>
<li>Processes where people create informal workarounds</li>
<li>Complaints that seem disproportionate to the size of the problem</li>
<li>Things that require knowledge from one specific person to navigate</li>
</ul>
<h2>Removing Friction</h2>
<p>If it isn't clear where the friction is in your company, and asking around hasn't helped, then a <a href="https://www.lean.org/the-lean-post/articles/understanding-the-fundamentals-of-value-stream-mapping/">value stream mapping exercise</a> can help you to understand the issue.</p>
<p>By carrying out this exercise you can map out your workflows and learn about the duration and effort it takes to complete each step. Walk through the steps with the people who actually do the work and look for steps that take a disproportionately long period of time, require multiple people or require re-work.</p>
<p>Focus on areas where there is wait time between activities or the same information needs to be gathered repeatedly are good candidates for removing friction.</p>
<p>The pattern across all these friction points is the transition cost - the overhead from moving between states. The highest-impact improvements often come from either eliminating these transitions entirely or making them so smooth they become invisible.</p>
<p>Where do you see friction in your organisation?</p>]]></content:encoded>
      <pubDate>Sun, 31 Aug 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Observability with Slack Workflows</title>
      <link>https://www.andismith.com/blogs/2025/08/observability-with-slack-workflows</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/08/observability-with-slack-workflows</guid>
      <description>I recently needed to keep an eye on a third party&apos;s rate limit during a product launch, and Slack Workflows seemed like a nice solution to alert me to issues. Let&apos;s take a look at how it worked.</description>
      <content:encoded><![CDATA[<p><strong>I recently needed to keep an eye on a third party's rate limit during a product launch, and Slack Workflows seemed like a nice solution to alert me to issues. Let's take a look at how it worked.</strong></p>
<h2>About Rate Limits</h2>
<p>Often when we use third party services, they come with some kinds of limits for the amount you are able to use them. This is often to stop abuse of their APIs and protect them from suffering denial of service attacks or service misuse.</p>
<p>Most third party services offer different tiers which can help you adjust your limits for different situations. For example:</p>
<ul>
<li>Auth0 has a tiered system based on monthly active users that can be adjusted month-by-month based on usage.</li>
<li>OpenAI has a comprehensive tiering system which scales with the amount you spend and the length of time you have had an account.</li>
<li>Analytics tools like Mixpanel offer tiers based on the number of events tracked which can be scaled per tier.</li>
</ul>
<p>Often catering for rate limits will involve picking a plan early or pre-paying for a certain amount of usage. While this seems helpful on the surface, it can result in <a href="/blogs/2025/06/keeping-saas-costs-down/">needing to pay for more capacity than you actually need</a>.</p>
<p>We were expecting to experience a short-lived spike in traffic which would result in us nearing our current plan limits. We'd created a couple of components which could be turned on when we were approaching a limit - but I wanted a way to also send myself and my team an alert during an unexpected spike so we could check everything was still running ok and potentially adjust our plans.</p>
<h2>Slack Workflows</h2>
<p>Slack provides great functionality for development teams to use automated messaging for observability and alerting. One of these tools is the workflow builder which allows you to POST data to an Slack webhook and that in turn can be used to post a message to a channel on your Slack organisation.</p>
<p>The message itself can include templated variables, emojis, @mentions and also a call to action button.</p>
<p>In my case, I wanted to alert how close I was to the limit, when it would be refreshed and include a link to a button that allowed me to configure the limit.</p>
<h2>Locating Rate Limits</h2>
<p>Different services provide different ways to retrieve rate limits. Lots of services like Auth0 and OpenAI include rate limit information in the response headers.</p>
<p>For example for OpenAI, they have comprehensive information on the state of your applications limits with fields such as:</p>
<ul>
<li><code>x-ratelimit-remaining-requests</code> - remaining allowed requests in the current timeframe</li>
<li><code>x-ratelimit-limit-requests</code> - max number of allowed requests in the current timeframe</li>
<li><code>x-ratelimit-reset-requests</code> - time until the request rate timeframe resets</li>
<li><code>x-ratelimit-reset-tokens</code> - time until the token limit timeframe resets</li>
<li><code>x-ratelimit-remaining-tokens</code> - remaining allowed tokens in the current timeframe</li>
<li><code>x-ratelimit-limit-tokens</code> - max number of allowed tokens in the current timeframe</li>
</ul>
<p>As another example, in Auth0 they are:</p>
<ul>
<li><code>x-ratelimit-remaining</code> - remaining requests in the current time frame</li>
<li><code>x-ratelimit-limit</code> - remaining allowed requests in the current timeframe</li>
<li><code>x-ratelimit-reset</code> - unix timestamp of when the rate limit will reset.</li>
</ul>
<h2>POST to Webhook</h2>
<p>In order to trigger the Slack workflow, we need to have a check in our code which reviews the limits and sends a message to Slack.</p>
<p>For example:</p>
<pre><code>const ALERT_TRIGGER_AMOUNT = 200;
const rateRemaining = parseInt(headers["x-ratelimit-remaining-requests"] ?? 0, 10);
const rateMax = parseInt(headers["x-ratelimit-limit-requests"] ?? 0, 10);
const rateReset = headers["x-ratelimit-reset-requests"];

// check for rateReset in case we get a header with no values back so we don't send a false alert
if (rateReset &#x26;&#x26; rateRemaining &#x3C; rateMax - ALERT_TRIGGER_AMOUNT) {
  console.warn("Warning: approaching limit", JSON.stringify(limits));

  // post to Slack hook
  await fetch(process.env.SLACK_HOOK_URL ?? "", {
    method: "POST",
    body: JSON.stringify({ rateRemaining, rateMax, rateReset }),
  });
}
</code></pre>
<p>In this code we check the remaining requests and if it's under 200 of the remaining amount, we send an alert.</p>
<h2>Creating a Slack Workflow</h2>
<p>To create a workflow within Slack:</p>
<ul>
<li>On Slack desktop go to your organisation's name in the top left and click the chevron.</li>
<li>Select Tools &#x26; Settings > Workflow Builder</li>
<li>Click the "+ New" button in the top right and choose "Build Workflow"</li>
</ul>
<div class="post-image">
<img alt="The workflow builder" src="https://www.andismith.com/assets/blogs/2025/08/observability-with-slack-workflows/slack-workflow.jpg">
Setting up a workflow
</div>
<ul>
<li>For step 1 - choose an event, select "From a webhook".</li>
<li>Enter the variables you wish to output in your message - e.g. <code>rateRemaining</code>, <code>rateMax</code> and <code>rateReset</code>.</li>
<li>For step 2 - you can choose to output a message to a channel.</li>
</ul>
<p>We can use the variables in our message like so:</p>
<pre><code>🚨 Warning! Reaching Limit @channel

RATES: {{{{rateRemaining}}}} out of {{{{rateMax}}}} (reset in {{{{rateReset}}}})
</code></pre>
<ul>
<li>Once we've created our workflow, a webhook will be created which we can then call from our application to trigger the workflow.</li>
</ul>
<p>And that's how it works.</p>
<p>Workflows are great for communicating monitors and lots of automated processes. For example, they can be used for:</p>
<ul>
<li>Deployment status updates with rollback buttons</li>
<li>Database performance alerts with direct links to monitoring dashboards</li>
<li>CI/CD pipeline failures with quick retry actions</li>
</ul>
<p>What other ways could you use Slack Workflows to improve your proactive response rates at work?</p>]]></content:encoded>
      <pubDate>Tue, 12 Aug 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Peacetime vs Wartime</title>
      <link>https://www.andismith.com/blogs/2025/08/peacetime-vs-wartime-cto</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/08/peacetime-vs-wartime-cto</guid>
      <description>Your monolith is buckling under heavy traffic growth. The quick fix is to beef up the server through vertical scaling and buy yourself six months. The right fix is a four month microservices migration that will either save the company or kill it if you miss the deadline. Meanwhile your $2m client is demanding new features or they&apos;ll go elsewhere.</description>
      <content:encoded><![CDATA[<p><strong>Your monolith is buckling under heavy traffic growth.</strong></p>
<p>The quick fix is to beef up the server through vertical scaling and buy yourself six months. The right fix is a four month microservices migration that will either save the company or kill it if you miss the deadline. Meanwhile your $2m client is demanding new features or they'll go elsewhere.</p>
<p>That's the kind of decision making situation Ben Horowitz captures in <a href="https://amzn.to/3JbiVqZ">The Hard Thing About Hard Things</a>. The book is a thoroughly insightful read on the challenges that face CEOs and a recurring theme throughout the book is the contrast between a peacetime CEO and a wartime CEO.</p>
<p>Peacetime usually occurs when a company has a large advantage against its competitors in its core market, and its market is growing. The company can focus on expanding the market and reinforcing its strengths.</p>
<p>Wartime usually occurs when a company is fending off an imminent existential threat from competition or a dramatic economic change.</p>
<p>The core philosophy in the book is that during peacetime, leaders must maximise and broaden the current opportunity using techniques to encourage broad-based creativity across diverse objectives. Whilst in wartime, the company must hit the target - survival depends on strict adherence to the defined mission.</p>
<h2>Applying this Philosophy to CTOs</h2>
<p>Reading this book led me to think about how this applies to CTOs. For many of the points made in the book about CEOs, they can be applied equally to CTOs. But there are some additional peacetime/wartime scenarios that are worth thinking about:</p>
<h3>Strategy</h3>
<ul>
<li>Peacetime CTO balances new feature development with platform improvements. Wartime CTO ships only features that directly impact survival metrics.</li>
<li>Peacetime CTO invests in monitoring, automation processes and scalable infrastructure. Wartime CTO focuses on keeping the lights on with minimal viable infrastructure and manual processes if needed.</li>
<li>Peacetime CTO plans for disaster recovery and business continuity. Wartime CTO takes on risk if it buys another day of survival.</li>
</ul>
<h3>Technology</h3>
<ul>
<li>Peacetime CTO encourages experimentation with new technologies and proof of concepts. Wartime CTO sticks with known, boring technologies.</li>
<li>Peacetime CTO may opt for a microservices architecture. Wartime CTO accepts a monolithic solution to ship faster.</li>
<li>Peacetime CTO lets the team decide their technical stack. Wartime CTO makes immediate decisions based on the fastest outcome and avoids unnecessary rewrites and refactoring.</li>
<li>Peacetime CTO allows for experimenting with the best tool for the job. Wartime CTO optimises for cost as if every penny were coming out of their own pocket.</li>
</ul>
<h3>Team</h3>
<ul>
<li>Peacetime CTO guides the team through career development and improvement. Wartime CTO hires and fires without hesitation to get the right team.</li>
<li>Peacetime CTO invests in developer experience and tooling. Wartime CTO tolerates developer friction if it means faster time to market.</li>
</ul>
<h3>Security</h3>
<ul>
<li>Peacetime CTO implements comprehensive security frameworks and compliance protocols. Wartime CTO goes for "good enough" to not get breached while moving fast.</li>
</ul>
<h2>Scenarios</h2>
<h3>Wartime Scenarios</h3>
<ul>
<li>A startup trying to find their product market fit with limited funding.</li>
<li>An e-commerce platform during Black Friday with revenue on the line.</li>
<li>A fintech startup racing to achieve compliance before funding runs out.</li>
<li>A company during a viral growth spike that's breaking infrastructure.</li>
</ul>
<h3>Peacetime Scenarios:</h3>
<ul>
<li>A dominant streaming platform, focusing on global infrastructure.</li>
<li>A successful enterprise software company investing in developer productivity.</li>
<li>A mature startup with product-market fit optimizing for long-term scalability.</li>
</ul>
<p>To understand what mode you should be in, you need to be taking regular assessments of where your business is at internally and within the market. How much runway do you have? Who are your biggest competitors? Is your revenue trending upward?</p>
<h2>The Adaptive CTO</h2>
<p>To be an effective CTO you need to be skilled at recognising which mode you are in and adapting your leadership style accordingly.</p>
<p>Your technical judgement needs to be good enough to understand what are real vs perceived risks. You need to know when your team are pushing for improving technical debt that will actually cause pain vs code that is ugly but functional.</p>
<p>Communicating under pressure is key to being able to successfully operate. You'll need to translate complex technical issues into language understood by stakeholders and be comfortable with difficult conversations about trade-offs.</p>
<p>You'll also need to be an expert at reading team morale and understanding the teams' stress levels. You'll need to know when you can push them hard vs when you need to give them space. To do that, you need to build relationships and trust that persists through both good times and in times of crisis.</p>
<p>There are some practical exercises in scenario planning, decision training and communication style training that can help you to build up these skills. For example:</p>
<ul>
<li>If you had to cut costs by 50% tomorrow, what would you do?</li>
<li>Make a technical decision with incomplete information</li>
<li>Write a status update in the tones of "everything is under control" and "urgent action needed".</li>
</ul>
<p>The best CTOs develop an instinctive ability to sense when the environment is shifting and proactively adjust their leadership style before being forced to by circumstances. If you look at your roadmap today, are you acting like it's peacetime or wartime? ...and are you absolutely sure you are in the right mode?</p>
<p><em>Please note: As an Amazon affiliate I earn from qualifying purchases.</em></p>]]></content:encoded>
      <pubDate>Sat, 09 Aug 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>From Sprawl to Structure</title>
      <link>https://www.andismith.com/blogs/2025/07/from-sprawl-to-structure</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/07/from-sprawl-to-structure</guid>
      <description>The business is growing, and your engineering team is too. Great news - until the sprawl sets in. How can your teams and processes scale with consistency and maturity?</description>
      <content:encoded><![CDATA[<p><strong>The business is growing, and your engineering team is too. Great news - until the sprawl sets in. How can your teams and processes scale with consistency and maturity?</strong></p>
<p>In a previous role, I inherited a fragmented system written in five different languages, both monolith and microservices; and years of inconsistent tech choices including areas of the system that had been completely neglected.</p>
<p>Whilst there were bottlenecks between the teams, the individual team performance ranged from moderate to good - so it was important not to be disruptive to the current ways of working of those productive teams with top-down mandates or heavy-handed governance that teams would try to circumvent or avoid. It felt like every team was solving the same problems differently - and sometimes unknowingly repeating each other's mistakes.</p>
<p>In this post I cover my experiences introducing lightweight governance, ensuring consistent architecture, and deprecating legacy systems without stalling team velocity.</p>
<h2>Architectural Consistency Through Standards, Not Mandates</h2>
<p>With an inherited system lacking process and documentation, there's no time like the present to start righting some of the wrongs.</p>
<p>One of the common issues we came across was that we often didn't understand <em>why</em> a particular technical choice had been made. So we began by documenting any new decisions in an <a href="https://github.com/joelparkerhenderson/architecture-decision-record">Architecture Decision Record</a>. These ADRs captured any significant architectural decisions made, including the context and consequences of those decisions. These could later be referred to by developers in other teams - or our successors - so they can understand the reasoning for different approaches and decisions.</p>
<p>I also brought developers together to review the current architecture and focus on patterns we'd like to use to improve future work. Given the varied tech stack, enforcing tools wasn’t feasible. But we could standardise how we build, test and deploy to make it easier for developers to switch between teams and reduce the debate when building new systems by aligning our new solutions to choices that aligned with our principles.</p>
<h2>Legacy-Innovation Balance via Strategic Modernization</h2>
<p>With a growing business, it's inevitable that technical debt will start to build up and some areas of your product will become legacy systems. Legacy systems aren’t always bad - but unexamined legacy is risky. We evaluated each system: should it be retired, kept or re-written?</p>
<p><a href="https://www.geeksforgeeks.org/system-design/event-storming-system-design/">Event storming exercises</a> allowed us to understand the key concepts of legacy systems and helped clarify business rules. Understanding what the priorities are with legacy systems will help you understand how to approach the problem.</p>
<p>The <a href="https://martinfowler.com/bliki/StranglerFigApplication.html">strangler fig pattern</a> let us replace functionality incrementally by wrapping legacy systems with a facade layer. Once all functionality is moved, you can retire the old system completely. With this pattern you never have a "big bang" replacement that could break everything and you can validate the new architecture with small pieces before committing fully. If issues do arise, you can easily rollback and redirect traffic back to the legacy system.</p>
<h2>Governance That Accelerates Rather Than Gates</h2>
<p>It is difficult to introduce new governance methodologies to teams without resistance.</p>
<p>It's important to bring the team on the journey to maturity and help them understand why these processes need to be introduced. Rather than leading with "we need governance", start conversations around the problems your developers are already experiencing. "We keep having to fix the same security issues across teams" or "onboarding new developers takes forever because nothing is documented" resonates more than abstract governance concepts.</p>
<p>Getting the team involved in how they can be implemented can also help. Forming working groups where senior developers from different teams help design the standards with their collective wisdom will lead to better buy-in. When people help create the rules, they're more likely to follow them, and they can help evangelize them to their teams.</p>
<div class="post-image">
<img alt="A team working together on the governance puzzle" src="https://www.andismith.com/assets/blogs/2025/07/from-sprawl-to-structure/governance.jpg">
Teamwork makes the dreamwork!
</div>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Shift-left_testing">Shift-left practices</a> such as including static analysis security scanning and basic compliance checks in pull request and deployment CI/CD workflows can help introduce governance early into your processes, and are something the team will quickly get used to as part of their everyday.</li>
<li>Infrastructure as Code can help bring in and keep these additions consistent - we created deployment templates with security scanning built-in, making the secure path the easy path.</li>
<li>Providing self-service platforms with the right guardrails and minimal friction can help in other areas, like setting up new repositories or managing production access.</li>
<li>Keep approvals lightweight so you meet your auditing requirements without blocking the velocity of the team.</li>
</ul>
<p>The goal for governance should be to make the secure, scalable golden path the easy one.</p>
<h2>Measuring Outcomes</h2>
<p>Like all parts of software development, it's important to measure the impact of the processes you put in place rather than the adherence and compliance to the process.</p>
<p>As well as measuring the maturity of your system and processes, measure how your development experience has changed. Some metrics to consider include: deployment frequency, lead time for changes, or incidents caused by architectural inconsistencies.</p>
<p>Are things getting better? If not, iterate.</p>
<h2>Summary</h2>
<p>As a CTO, you're responsible for guiding the platform to scale sustainably - without slowing the team down.</p>
<p>It's important to focus on practical, proven approaches like the ones described in this article to help you move the system forward, and ensure the team are invested in achieving the results you need.</p>
<p>Don't try to fix it all at once - start small. Introduce lightweight governance, document decisions and build the golden path that teams will want to follow. Start with one visible win such as documenting architectural decisions or adding security scanning to CI/CD, then measure impact before expanding governance practices to other areas. By maturing your systems gradually, your team will scale with it.</p>
<p>Where could you introduce clarity without friction this quarter?</p>]]></content:encoded>
      <pubDate>Tue, 08 Jul 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Ship Fast, Scale Clean - Building MVPs That Last</title>
      <link>https://www.andismith.com/blogs/2025/07/ship-fast-scale-clean</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/07/ship-fast-scale-clean</guid>
      <description>Your founder comes to you with the classic Minimum Viable Product (MVP) dilemma. We need to build quickly for speed to market, but make it maintainable so it can scale as we grow.</description>
      <content:encoded><![CDATA[<p><strong>Your founder comes to you with the classic Minimum Viable Product (MVP) dilemma. We need to build quickly for speed to market, but make it maintainable so it can scale as we grow.</strong></p>
<p>Ah, you want to have your cake and eat it! <em>Brilliant.</em></p>
<p>Moving fast is one of the most advantageous tools in a startup's toolbox. MVPs primarily exist to help you learn, not just to launch, and by moving fast you can initiate the user feedback loop quicker and test your all-important product market fit.</p>
<p>But often quality is sacrificed for speed and many startups find themselves falling in to a "rewrite trap" as they need to scale and try to grow.</p>
<p>There are some steps we can take as CTOs to help. Let's explore them together!</p>
<div class="post-image">
<img alt="Picture of a ship sailing on the seas whilst still being constructed" src="https://www.andismith.com/assets/blogs/2025/07/ship-fast-scale-clean/mvp-ship.jpg">
Get moving whilst your still building!
</div>
<h2>Choose a Sensible Core Tech Stack</h2>
<p>Choose technologies that are proven and boring <em>to you</em> over the latest cutting-edge can help mitigate the risk of building applications. Optimise for developer experience and team familiarity over theoretical performance gains. By using technologies you and your team are already experts in, you've immediately removed a risk.</p>
<p>There's also a wealth of developers building in proven, common technologies which makes hiring cheaper and faster, and there's tons of tutorials and assistance online for when you get stuck.</p>
<p>Here's some boring technologies I usually go to:</p>
<ul>
<li>Languages: Node.js/Python/Go (base this on your team's expertise)</li>
<li>Database: PostgreSQL/DynamoDB</li>
<li>Cloud: AWS</li>
<li>Front End: React / React Native (Expo)</li>
</ul>
<h2>Build vs. Buy</h2>
<p>I've <a href="https://www.andismith.com/blogs/2025/05/buy-vs-build">previously written in detail</a> about Buy vs. Build strategies - and this all stands true at the MVP stage too.</p>
<p>Why put yourself through the additional stress and pain of building non-core functionality from scratch when software like Stripe and Auth0 exist?</p>
<p>Build only what differentiates your product, and make your decisions based on the question: <em>"Is this our competitive advantage?"</em>. If no, buy it.</p>
<h2>Making It Non-Throwaway</h2>
<p>By moving fast with existing knowledge and buying non-core functionality, you'll have far more time to invest in the things that are important for maintainable, foundational software. Following these three foundational rules can help set you on the right path to success:</p>
<h3>1. Database Design</h3>
<p>Your database is almost always the hardest foundation to change later. Spend time understanding how data should be structured and coming up with a good database design.</p>
<ul>
<li>Avoid duplicating data unnecessarily, but don't over-normalize either. Well-defined data relationships prevent data integrity issues that compound as you scale.</li>
<li>Understand how your application is using data and index your actual query patterns as well as your primary keys. A missing index will cause performance issues at surprisingly low user counts.</li>
<li>Keep consistent naming patterns for field names - e.g. use <code>user_id</code> not <code>userId</code>. Inconsistency creates bugs that waste debugging time.</li>
<li>Use data types that make sense for your product. Some decisions I typically make:
<ul>
<li>Use <code>UUID</code> for IDs.</li>
<li>Use <code>timestamp</code> for dates.</li>
<li>Use <code>integer</code> for money to avoid floating point math.</li>
<li>Regardless of your choices, note down your decisions and keep them consistent.</li>
</ul>
</li>
</ul>
<h3>2. API Design</h3>
<p>Ensure you have clean API boundaries - mobile and web apps will depend on these contracts.</p>
<ul>
<li>Have consistent REST/GraphQL patterns such as ensuring resource names are always nouns and plural.</li>
<li>Utilise the proper HTTP methods for the task the API is performing.</li>
<li>Use the proper HTTP status codes.</li>
<li>Valid input at the boundary - don't let it into your system.</li>
</ul>
<h3>3. Good Logging</h3>
<p>Ensure you have comprehensive logging so that you give yourself the information to be able to debug production issues from day one.</p>
<ul>
<li>Ensure your logs are structured with consistent fields.</li>
<li>Use request tracing so you can follow flows through your stack.</li>
<li>Track feature usage so you understand what areas of your product are the most valuable.</li>
<li>Consider if you want to implement feature flags or A/B testing tools to test hypotheses quickly.</li>
</ul>
<h3>Other Considerations</h3>
<ul>
<li><strong>Follow the YAGNI principle.</strong> You Ain't Gonna Need It. Considering YAGNI with each of your decisions will assist in avoiding over-engineered solutions and spending too long on "what if" scenarios.</li>
<li><strong>Be ok with intentional technical debt.</strong> Document why certain decisions and compromises were made and they will need to change as you grow. For example, maybe for speed you start with a monolith rather than microservices. Microservices add complexity, more deployments and complicate service communication and orchestration. Decisions can be logged and tracked using a simple ADR (Architectural Decision Record).</li>
<li><strong>Planning for 10x growth is fine.</strong> Be reasonable with the amount of architectural decisions you make around scale. Instead, design for observability and change. If something starts to scale poorly, you will be well equipped to spot the warning signs. Then when you do have to worry about 100x growth, it's likely you will be better equipped to make the right decisions for your product.</li>
<li><strong>Set up the basics for good coding practices.</strong> With minimal effort, it's possible to get linting and static analysis in to the code base from day one. Using a Github Actions workflow on pull requests can quickly assess code quality and ensure you are moving in the right direction.</li>
<li><strong>Think about testing strategy.</strong> Consider basic unit/integration tests for core user journeys such as sign up flow and payment processing. These can validate that your MVP's functionality remains consistent through a period of constant change.</li>
</ul>
<h2>Summary</h2>
<p>Following the steps I've detailed here you can create a great MVP version of your app by learning quickly about your product market fit whilst still giving your product - and team - room to grow.</p>
<p>Well done, your founder is impressed! Enjoy the cake! 🍰</p>]]></content:encoded>
      <pubDate>Thu, 03 Jul 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Lessons from Startup Life</title>
      <link>https://www.andismith.com/blogs/2025/06/lessons-from-startup-life</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/06/lessons-from-startup-life</guid>
      <description>Some things I learnt during my time working as tech employee #1 at a startup.</description>
      <content:encoded><![CDATA[<p><strong>Some things I learnt during my time working as tech employee #1 at a startup.</strong></p>
<p>I have been reflecting on my learnings from the past few years of working at previous startups. As well as being incredible experiences, there were also a lot of learnings. Finding our product market fit was tricky in what became a difficult market and we naturally made a few wrong turns before ultimately ending up in a better place. But I've learnt and grown so much from the role - a smooth sea never made a skilled sailor.</p>
<p>Every startup journey has its unique challenges and victories - these are some universal lessons I've picked up along the way. In no particular order, here are some of my thoughts:</p>
<ul>
<li>
<p><strong>How will your experience enable people to tell stories?</strong> - To have a successful product, you need to have people talking about it. So take a step back and think about how a user will feel using the product and who they will want to tell about it. It can really help focus the mind on what the product should be in the first place.</p>
</li>
<li>
<p><strong>Be focused</strong> - Trying to do lots of things quickly at the same time leads to a poor product, so managing ambition is key. You're better off doing one thing really well. It can be difficult to kill features, but it's really important to do as it'll add drag to every decision and every pivot.</p>
</li>
<li>
<p><strong>Indulge one client</strong> - Start with a single client and work with them until they love your product. Get to the point where they can't live without your product and then start to expand. Lots of clients make lots of noise, and then once you have a mass of clients it's a lot harder to change and pivot.</p>
</li>
<li>
<p><strong>Make sure you are aligned on what success is</strong> - What does a successful business look like? Some businesses thrive on daily active users, whilst others just need a few power users who can't live without your product. Make sure the data your tracking and reporting on reflects these success metrics.</p>
</li>
<li>
<p><strong>Make it easy</strong> - Once you have your product market fit adding extra benefit can be hard. Sometimes the best way to benefit users is to make things easier to use. How can you cut down the time it takes for a user to go from point A to point B? Make it as simple as possible to use, reduce the possibility of drop-off.</p>
</li>
</ul>
<div class="post-image">
<img alt="People learning from data" src="https://www.andismith.com/assets/blogs/2025/06/lessons-from-startup-life/cover.jpg">
Lessons learnt == Experience gained
</div>
<ul>
<li>
<p><strong>No is a decision, yes is a responsibility</strong> - It can be hard to say no, but if you never say no you will inevitably find yourself in situations you regret. You rarely regret saying no. So think about the things you do want to say yes to - what's going to make the most impact? And remember, yes leads to extra work, and it may not be the right time to do that work.</p>
</li>
<li>
<p><strong>Size doesn't matter</strong> - The bigger your company, the more impressive you sound - but working with large amounts of people adds challenges. Startups are meant to be small and nimble - so avoid huge growth spurts early on, and certainly add hires slowly and steadily. You likely need less people than you think.</p>
</li>
<li>
<p><strong>Don't break the flow</strong> - I found I was working far better in the early hours of the evening - as that's when I could actually focus my job without distractions. Interruptions break your concentration, they stop you doing the job you need to do. I noticed that our velocity was dropping, so we agreed to move as many meetings as we could to the mornings so developers had their afternoons free. I also looked at what meetings could be handled asynchronously instead - for example, doing some stand-ups as Slack messages or recording Looms of things you need people to watch but not participate in means they can fit them around their work day.</p>
</li>
<li>
<p><strong>Everyone contributes</strong> - When you have a small team, you need contributors not delegators - including the leaders. Personally, I'm not afraid to get stuck in with whatever job needs doing - even if it isn't my job at all. It helps you understand the hires you will need to eventually make as you are doing jobs you will one day need to hire for. Delegators often love meetings as that's where they feel important - and their meetings have a habit of spawning more meetings - which cause even more interruptions.</p>
</li>
<li>
<p><strong>Trust your gut... and the data</strong> - Often your gut is right, and if you love your product your audience will also love it. But - if the data is overwhelming, then kicking off a quick experiment to explore alternatives is totally worth the damage to your ego.</p>
</li>
</ul>]]></content:encoded>
      <pubDate>Wed, 25 Jun 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Inkscape 101</title>
      <link>https://www.andismith.com/blogs/2025/06/inkscape-101</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/06/inkscape-101</guid>
      <description>I&apos;ve been playing around with Inkscape for the first time this weekend. It&apos;s been fun and I&apos;ve managed to get some good results really quickly. Here&apos;s my 101 intro to Inkscape! From one beginner to another beginner!</description>
      <content:encoded><![CDATA[<p><strong>I've been playing around with Inkscape for the first time this weekend. It's been fun and I've managed to get some good results really quickly. Here's my 101 intro to Inkscape! From one beginner to another beginner!</strong></p>
<p>I've always wanted to be able to create my own SVGs but the barrier to entry has always felt too high - as soon as we start having to deal with bezier curves I feel like I'm way out of my depth. This weekend, I finally bit the bullet and <a href="https://inkscape.org/">downloaded Inkscape</a>, and discovered that it's actually really straightforward to get good results and you don't have to get bogged down with math. Sharing's caring (and I wanted to write notes) - so here's some of the things I have learnt.</p>
<div class="post-image">
<img alt="Inkscape" src="https://www.andismith.com/assets/blogs/2025/06/inkscape-101/inkscape.webp">
Art by Inkonic from the Inkspace home page - I wish I could draw this well! 
</div>
<h2>Keyboard Commands > Interface</h2>
<p>There are a lot of options in Inkscape - it's obviously very powerful. I've been using keyboard shortcuts as much as possible as it's easier to navigate than multiple menus. So a lot of the advice here revolves around which keys to use.</p>
<h2>Zooming In</h2>
<p>So I loaded Inkscape and the first thing that struck me was my canvas was really far away! I couldn't see anything. The usual keyboard shortcuts I'd use in other programs didn't work. It was clear I was going to have to learn new ones.</p>
<ul>
<li>By far the most useful, pressing <code>z</code> + <code>5</code> zooms the canvas to fit your window size - so you can see everything in one go.</li>
<li><code>z</code> + <code>+</code> zooms in and <code>z</code> + <code>-</code> zooms out.</li>
</ul>
<h2>Drawing Something</h2>
<p>If you want to draw a circle (press <code>e</code> for ellipse) or a square (<code>r</code> for rectangle) there are some nice, easy to understand tools to help you do that. But if you want to draw a custom shape, you're going to need to work with paths.</p>
<p>The pen tool (keyboard shortcut <code>b</code>) help you draw paths and selecting the "BSpline path" option from the top "Move" menu means you can click away adding as many nodes as you want without having to worry about your curves. The tool will do it's best to smooth out your lines for you letting you just worry about where your shape is going. Double click or hit <code>enter</code> to finish the path.</p>
<p>It'll probably look a little rough if you've used a lot of nodes and you may want a more curved look. Once your path is finished you can hit <code>command/ctrl</code> and <code>l</code> to simplify your path which has a nice side effect of making your path look more natural!</p>
<h2>Transform</h2>
<p>Within Inkscape, you can scale and rotate your paths and objects. Hitting <code>shift</code>, <code>command/ctrl</code> and <code>m</code> will bring up the transformation panel. I guess m stands for matrix?</p>
<p>It's a bit weird compared to changing values in CSS or other tools - once the transformation is applied, you need to think from zero again as all future actions are additive. For example, in CSS if I wanted to rotate an element I'd use 90deg, and to change it I may update it to 105deg. In Inkscape if I originally did 90deg and wanted the same change, I'd need to apply 15deg to get the same result.</p>
<p>To get a better idea of positioning on your page, View > Page Grid is a nice way to get a grid and then you can line things up.</p>
<h2>Symmetry</h2>
<p>There's a few ways to achieve symmetry in Inkscape.</p>
<p>There is a mirror symmetry mode where you can mirror your path. As a beginner, I've struggled a bit with this when it comes to placement.</p>
<p>Instead, I've had better success with Duplicate (<code>command/ctrl</code> + <code>d</code>). Once you have a clone, pressing <code>h</code> will horizontally flip your selected object, and <code>v</code> will vertically flip it.</p>
<p>Once you've duplicated, mirrored your path and moved it in to the right place you'll probably be left with some sharp bits where the two objects meet. Inkscape has a really nice tool to help with this.</p>
<p>First, select the Node tool (keyboard shortcut <code>n</code>) and <code>shift</code> and select both paths. Then <code>shift</code> select the end node of each path that you would like to join. There press the 'Join selected nodes' menu item in the top menu. This will join your paths and smooth them out.</p>
<p>In case mirror symettry is your thing, you can try this by going to Path > Path Effects, add Mirror Symmetry effect and then Path > Object to Path to convert it to a path.</p>
<h2>Cropping</h2>
<p>Once you have made your image, you can reduce the size of your viewport by selecting your objects and pressing <code>shift</code>, <code>command/ctrl</code> + <code>r</code>, or go to the Edit menu and hit "Resize Page to Selection".</p>
<h2>Exporting a Single Layer</h2>
<p>By default, Inkscape will save your SVG with all the layers you've created - even the hidden ones. Which is good - you probably wanted them for later.</p>
<p>But when you do only want to keep certain objects, in the Layers panel select the layer you want to export and all the objects in that layer. Go to File > Export (or <code>shift</code>, <code>command/ctrl</code> + <code>e</code>) and choose the 'Selection' tab. Tick the box that says "Exported Selected Only", choose "Plain SVG" as the file format and then hit "Export". You can <a href="https://optimize.svgomg.net/">SVGOMG</a> the results for smaller file sizes.</p>
<h2>We're Just Getting Started!</h2>
<p>I hope you've found these tips useful. I'm going to continue playing around with Inkscape, so if there's any useful beginner tips you think I should know - please drop me a message!</p>]]></content:encoded>
      <pubDate>Sun, 22 Jun 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Intro to SOC 2 for CTOs</title>
      <link>https://www.andismith.com/blogs/2025/06/intro-to-soc-2-for-ctos</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/06/intro-to-soc-2-for-ctos</guid>
      <description>Congratulations! You&apos;ve reached the point in your business where you are mature enough to start thinking about cybersecurity and security compliance, but there&apos;s a lot of new information to get your head around.</description>
      <content:encoded><![CDATA[<p><strong>Congratulations! You've reached the point in your business where you are mature enough to start thinking about cybersecurity and security compliance, but there's a lot of new information to get your head around.</strong></p>
<p>SOC 2 certification is achieved by proving that certain security controls are in place and working as intended. While this sounds straightforward, the reality involves months of preparation, company-wide coordination, and careful evidence collection that many growing companies underestimate.</p>
<p>In this article, I share my experiences with SOC2 and the things I learnt along the way.</p>
<h2>Type 1 vs Type 2</h2>
<p>Firstly, there are two types of SOC 2 certification. Type 1 is promising the things you will do and Type 2 is proving you did them. Most companies expect their vendors to be Type 2 compliant, but it can take months to be ready for audit and then further months to collect evidence to prove you are compliant. A SOC 2 Type 2 audit period of 6 months or higher is generally considered acceptable - although it is possible to get shorter certificates.</p>
<h2>To Infinity And Beyond</h2>
<p>Once you are SOC 2 compliant, you need to stay SOC2 compliant. You will be audited every year, so it's important to put the checks and balances in place to ensure as you grow you will remain compliant. And you'll need an annual refresh for documentation and evidence.</p>
<h2>Trust Criteria</h2>
<p>There are five different trust criteria for SOC2:</p>
<ul>
<li>Security</li>
<li>Availability</li>
<li>Confidentiality</li>
<li>Processing Integrity</li>
<li>Privacy</li>
</ul>
<p>Important: You do not need to be compliant in all five trust criteria! In fact most companies are not!</p>
<p>Only security is compulsory.</p>
<p>Availability and Confidentiality trust criteria are also common. Processing Integrity and Privacy require more rigorous checks and more expensive processes and monitoring.</p>
<p>It's critical to carefully scope your audit and understand what your business needs. Over-scoping can easily double your compliance burden.</p>
<h2>It CANNOT Be Soloed</h2>
<p>Let me repeat. It cannot be soloed (unless you are a company of 1). SOC 2 requires the whole company to take it seriously. It is everybody's responsibility to keep the company secure, it cannot be one person's responsibility.</p>
<p>There are parts of SOC 2 which are operations, HR, recruitment and even the board of directors. Some of the toughest parts of achieving compliance are trying to get buy in from the entire business.</p>
<h2>Processes</h2>
<p>As a CTO, there are processes you can put in place early to make passing the audit easier later on:</p>
<ul>
<li>A SDLC process</li>
<li>CI/CD pipelines</li>
<li>Mandatory Pull Request Reviews</li>
<li>Linking JIRA Tickets to PRs</li>
<li>Security Scanning (SonarCloud makes this easy)</li>
<li>Use a Password Manager</li>
</ul>
<h2>Cheat Mode</h2>
<p>I've carried out SOC2 processes in spreadsheets and also with specialist compliance tools such as <a href="https://www.secfix.com/">SecFix</a>, <a href="https://drata.com/">Drata</a> and <a href="https://www.vanta.com/">Vanta</a>. The specialist tools seem expensive. But please trust me, they are more than worth it. These tools often allow you to automate certain processes to prove compliance, and to store your compliance data in a single place.</p>
<p>As I mentioned, these tools can be pricey - but if you shop around you can haggle a decent price. They are all fairly level in terms of feature set, so you can use this to your advantage.</p>
<h2>Audits</h2>
<p>Start collecting evidence from day one of your audit period, not months in. I've seen companies scramble to find 6+ months of missing evidence. This is not the way. Ensure you are collecting and know where to find your evidence from the start.</p>
<p>As well as tooling, you'll also need to pay for an auditor. A lot of compliance tools have recommended auditors who know their tooling well.</p>
<p>Toward the end of the audit period, your auditors will want to interview the team and look at the evidence you have gathered. This is a whole team effort, and auditors will want to see that the whole company is interested in security - not just technology.</p>
<h2>It's not a Pass or Fail</h2>
<p>Unlike a driving test, you cannot pass or fail a SOC 2 audit. You can do badly in it, but an auditor won't fail you. Instead, at the end of an audit the SOC2 auditor will list the gaps that they have found and you will be given a chance to respond to them. This report will be what you share whenever a client, customer or vendor requests it.</p>
<p>Whilst it's not great news for an auditor to find some gaps, it is common. I've seen many SOC2 reports with observations of controls that were not passing.</p>
<p>The important factor is to ensure you don't repeatedly fail for the same issues. Make sure you budget for remediation time. Even with good preparation, you'll likely need to address some findings post-audit.</p>
<h2>Keep an Eye on Onboarding</h2>
<p>In my experience the area that is most likely to fail an audit is the Employee Onboarding experience. A new employee who hasn't signed the code of conduct, the acceptable use policy, or completed the mandatory security training can catch you out. And unfortunately, an auditor has to report it.</p>
<h2>What about ISO 27001?</h2>
<p>While SOC 2 primarily focuses on proving you've implemented security controls that protect customer data, ISO 27001 also asks you to prove you have an operational information security management system (ISMS) in place to manage your InfoSec program on a continual basis.</p>
<p>In my experience, SOC 2 is a good stepping stone to ISO 27001 certification. ISO 27001 seems to be better regarded as a compliance standard in Europe, whilst SOC 2 is more popular in the US.</p>
<h2>Conclusion</h2>
<p>As you can see from this article, SOC 2 compliance is quite involved and requires a lot of planning and preparation. It's valuable once complete, but ensure you are prepared for the investment it carries.</p>]]></content:encoded>
      <pubDate>Mon, 16 Jun 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Keeping SaaS Costs Down</title>
      <link>https://www.andismith.com/blogs/2025/06/keeping-saas-costs-down</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/06/keeping-saas-costs-down</guid>
      <description>It&apos;s very easy to fall in to the trap of tool sprawl and escalating costs - particularly in the high speed world of startups. Let&apos;s take a look at some techniques we can use to keep a handle on it.</description>
      <content:encoded><![CDATA[<p><strong>It's very easy to fall in to the trap of tool sprawl and escalating costs - particularly in the high speed world of startups. Let's take a look at some techniques we can use to keep a handle on it.</strong></p>
<p>Tool sprawl is the accumulation of multiple tools which serve the same or similar purposes. Throughout any company's lifetime it's not uncommon to have disconnected departments using similar tools without even realising it. Suddenly you are paying for multiple SaaS contracts and your costs have doubled (or worse). Left unchecked, these costs can quietly bleed thousands from your burn rate - money that could be better spent on product development or growth.</p>
<h2>Start with Monthly Plans</h2>
<p>SaaS organisations are always in a hurry to get you to sign annual (or longer) contracts to lock you in. These deals look attractive as over the long term they will likely save you money. However you lose flexibility - and when your company is still figuring out its identity locking in to a service prematurely may result in spend in an area you no longer need to be spending in.</p>
<p>Enterprise plans often come with premium price tags — justified by SLAs (Service Level Agreements) that promise guaranteed uptime, priority support, and faster response times. But in the early stages of your startup, you may not actually need these guarantees.</p>
<p>Before locking yourself into a costly Enterprise contract, evaluate whether the promised SLAs are truly critical to your team’s success. Many smaller startups can operate perfectly well on standard or pro-tier plans.</p>
<p>Take a moment to review the service’s status page (most providers have one) and check their historical uptime and incident frequency. A well-run SaaS company with a solid track record may offer sufficient reliability without the need for an expensive support contract.</p>
<p>Some tools (like Auth0 and Sentry) allow you to adjust your expenses month by month which can help you save extra money during quieter months (e.g. if your app gets more traffic around the holiday season). It's also worth noting that most tools won't switch you off when you exceed capacity and provide a short period of grace so you can adjust your requirements.</p>
<h2>SaaS Auditing</h2>
<p>Schedule quarterly reviews of all your subscriptions. This isn't just about finding forgotten tools - it's about evaluating whether each service still provides value proportional to its cost. Create a simple scoring system: usage frequency, business impact, and cost per user or transaction. Tools that score poorly across these metrics are candidates for replacement or elimination.</p>
<p>During these audits, also check for seat optimisation. Many SaaS tools charge per user, and it's common to have inactive accounts for former employees or users who've moved to different roles. A monthly seat audit can save significant money over time.</p>
<h2>Capacity Tracking</h2>
<p>One technique I use to manage non-seat SaaS tools is to track the capacity usage of our vendors so I can record and see how usage varies month on month. This provides predictability for the business, ensures we remain on the right plans and gives me the benefit of negotiating early when services need to scale.</p>
<p>For example:</p>
<table class="post-table">
  <thead>
      <tr>
          <th>Vendor</th>
          <th>Current Capacity</th>
          <th>Plan Allowance</th>
          <th>RAG</th>
          <th>Action Required?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Auth0</td>
          <td>890 MAU</td>
          <td>1,000 MAU</td>
          <td><span style="color: orange">Amber</span></td>
          <td>Adjust to next plan level</td>
      </tr>
      <tr>
          <td>Sentry</td>
          <td>1,500 errors</td>
          <td>50,000 errors</td>
          <td><span style="color: green">Green</span></td>
          <td>—</td>
      </tr>
      <tr>
          <td class="vendor-name">etc</td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
      </tr>
  </tbody>
</table>
<h2>Create a Spreadsheet</h2>
<p>Everyone's favourite friend - the Excel / Google Sheets spreadsheet. As much as we mock it, it allows you to stay on top of the costs for your tools early on.</p>
<p>I tend to track a list of the tools, departments, cost type, frequency and how much is estimated/spent per month. Cost type is either Cost of Goods - costs directly tied to your product's delivery like the app, infrastructure or APIs; or Expenses which covers tools used for operations.</p>

  <table class="post-table">
  <thead>
      <tr>
          <th>Tool</th>
          <th>Description</th>
          <th>Dept.</th>
          <th>Cost Type</th>
          <th>Frequency</th>
          <th>Jan</th>
          <th>Feb</th>
          <th>...</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Auth0</td>
          <td>Auth Provider</td>
          <td>Tech</td>
          <td>Cost of Goods</td>
          <td>Monthly</td>
          <td>$x</td>
          <td>$x</td>
          <td>$x</td>
      </tr>
      <tr>
          <td>Sentry</td>
          <td>Error Reporting</td>
          <td>Tech</td>
          <td>Expense</td>
          <td>Monthly</td>
          <td>$x</td>
          <td>$x</td>
          <td>$x</td>
      </tr>
      <tr>
          <td>etc</td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
      </tr></tbody>
</table>
<h2>Encourage a "New Tool" Process</h2>
<p>While you don't want to slow down your startup, having a short process that requires an employee to request a tool before it can be expensed ensures that the usage of the tool has been well thought out and keeps adoption disciplined.</p>
<h2>Look out for Freebies</h2>
<p>If you are a startup, freebies are everywhere. You just need to look for them. Often your startup investors will be able to provide codes and links to get you started. The cloud platforms are particularly keen on AI startups at the moment and may have additional discounts for them.</p>
<p>Here are some of the discounts available at the time of writing:</p>
<ul>
<li><a href="https://pages.awscloud.com/APJ-startup-PT-AWS-Activate-2022-reg.html">AWS Activate</a> offers a wide range of benefits for startups - including up to $100k in AWS credits and discounts on other tools.</li>
<li><a href="https://github.com/enterprise/startups/">Github</a> offers a year of free Enterprise access for up to 20 seats.</li>
<li><a href="https://startup.google.com/">Google</a> offers a number of programs for the Google Cloud Platform.</li>
<li><a href="https://www.microsoft.com/en-gb/startups">Microsoft</a> also offer a number of startup discounts for Azure.</li>
<li><a href="https://help.miro.com/hc/en-us/articles/360014912819-Miro-for-startups">Miro</a> offers a $1000 startup credit - which is roughly a year of Miro access for a small team.</li>
<li><a href="https://retool.com/startups">Retool</a> is great for quickly building admin panels for your application, and offers startup credits up to $60k.</li>
<li><a href="https://stripe.com/gb/startups">Stripe</a> offer a startup program with access to financial benefits and experts to help you grow.</li>
<li>Some investors have their own portals for providing additional discounts, so do speak to them.</li>
</ul>
<p>Don't forget about open source alternatives either. Tools like Grafana, Supabase, and PostHog offer robust open source versions that can significantly reduce costs while you're building product-market fit.</p>
<h2>Live a Task (and Look to Automate) Before Hiring</h2>
<p>Hiring people is expensive, and hiring the wrong people is even more expensive. In a startup it's essential to get the right fit. Before hiring for a role to fulfill a task, actually carry out the task yourself so you can understand the skills you need to make the right hire. And if at all possible, use Saas tools to automate the task rather than hiring.</p>
<p>Look at tools like <a href="https://zapier.com/">Zapier</a> to easily automate workflows between SaaS tools. Sometimes a $50/month automation tool can replace a $5000/month hire, at least in the early stages.</p>
<h2>Negotiate Everything</h2>
<p>Don't accept the first price you see. Most SaaS companies have flexibility, especially for annual contracts or when you're close to moving to a higher tier. Come prepared with usage data, competitor pricing, and a clear understanding of your growth trajectory. In the past I've managed to get discounts that have even surprised me simply by asking multiple times.</p>
<p>For critical tools, consider negotiating contract terms beyond just price: cancellation clauses, feature guarantees, and support SLAs can be just as valuable as discounts.</p>
<h2>In Summary</h2>
<p>SaaS cost management isn't about being cheap - it's about being intentional.</p>
<p>Every tool should earn its place in your stack through clear value delivery. Your startup is likely moving at 100mph, so new SaaS tools are introduced quickly and frequently. But regular audits, careful capacity planning, and strategic consolidation will keep your costs predictable and your team productive.</p>
<p>Remember that the goal isn't to minimize SaaS spend at all costs, but to ensure every dollar spent contributes meaningfully to your business outcomes. A well-chosen $200/month tool that saves 10 hours of developer time is infinitely more valuable than a free alternative that creates friction and technical debt.</p>
<p>By implementing these practices early you'll keep ahead of the tool sprawl trap that catches so many young and growing businesses.</p>]]></content:encoded>
      <pubDate>Sun, 15 Jun 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>6 Types of CTO</title>
      <link>https://www.andismith.com/blogs/2025/06/6-types-of-CTO</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/06/6-types-of-CTO</guid>
      <description>If you examine different CTO job roles currently on job boards, you&apos;ll find that there&apos;s no established job spec for a CTO. The role varies dramatically depending on the type of company. A CTO may be hands-on in one company, and a &quot;big vision&quot; role in another.</description>
      <content:encoded><![CDATA[<p>If you examine different CTO job roles currently on job boards, you'll find that there's no established job spec for a CTO. The role varies dramatically depending on the type of company. A CTO may be hands-on in one company, and a "big vision" role in another.</p>
<p>Some companies also interchangeably use the CTO and VP, Engineering job roles. Bloom employed me as a VP, Engineering but my role often overlapped into areas outside engineering - such as data, security, vision, finance and stakeholder management.</p>
<p>After thinking about it some more, I believe there are multiple archetypes of CTO, and as a company evolves the CTO job role does too. Your mileage may vary - but in my mind the CTO job is actually six different roles depending on the stage of the company.</p>
<p>Not every CTO walks in a straight line through these different archetypes. Some CTOs move fluidly between stages, while others find their niche. One might be a wizard at the early stage prototyping but step aside once the team hits 100 engineers. The most effective CTOs are those who know when to evolve, when to stay in their lane, and when to hand off the baton.</p>
<h2>1. The "Let's Get Started" CTO</h2>
<p>The "Let's Get Started" CTO is often either the technical co-founder or the first technical member of the team. When they join, there isn't a product - just an idea. The company is likely in Pre-Seed or Seed stage.</p>
<p>This CTO will:</p>
<ul>
<li>Make foundational technology choices that will impact the company for years.</li>
<li>Be a capable, self-starting technical engineer who can build working prototypes and pivot quickly to change, balancing technical debt with speed of delivery.</li>
<li>Prioritise Time to Market and finding Product Market Fit as these are critical for the business.</li>
<li>Be capable of working in multiple technical areas (such as infrastructure, front end, back end, IT, compliance).</li>
<li>Have a keen eye for first hires and be unafraid to make changes when personnel aren't working out.</li>
<li>Manage individual contributors.</li>
<li>Be the technical voice in fundraising conversations with investors.</li>
</ul>
<h2>2. The "Early Stage" CTO</h2>
<p>After the product has launched and started to gain traction, the company will begin to grow and the CTO role will too. Usually this occurs around Seed to Series A.</p>
<p>As the company will need further focus on process and team building, there may be a decision on whether the existing CTO continues in the role, or if they become a lead engineer.</p>
<p>This CTO will:</p>
<ul>
<li>Have a strategy for how the technical side of the company will develop.</li>
<li>Be responsible for designing the processes that will define the company's technical culture and outputs.</li>
<li>Delegate! They need to be capable of managing multiple teams and delegating day-to-day control to a senior within the team.</li>
<li>Establish metrics and KPIs aligned with business goals - e.g. uptime percentage, time-to-hire etc</li>
<li>Take part in budgets and recruitment processes.</li>
<li>Build relationships with key customers for technical discussions.</li>
<li>Balance longer term strategy with day to day requirements.</li>
<li>Ensure the system remains live and operational - and handle and resolve any downtime incidents.</li>
<li>Spend a lot less time in the codebase, and avoid the critical path!</li>
</ul>
<h2>3. The Scaling CTO</h2>
<p>Now the business has a clear Product Market Fit, the momentum will move to onboarding customers in the product rapidly.</p>
<p>Business focus moves to uptime and scalability. The codebase is a distant memory and the CTO's focus is on strategy, growing the business and technical architecture.</p>
<p>This CTO will:</p>
<ul>
<li>Understand and have a strategy for how to scale the architecture for growth.</li>
<li>Build partnerships with key technology vendors.</li>
<li>Have a hiring plan that allows for developer growth.</li>
<li>Implement incident response and on-call rotations.</li>
<li>Understand the security and data implications of having larger "enterprise" clients.</li>
<li>Influence the product roadmap based on technical constraints and opportunities.</li>
</ul>
<h2>4. The Expansion CTO</h2>
<p>With customers onboarded, the company will look to new paths for expansion - this could be acquiring other companies, adding new products or moving to other markets.</p>
<p>At this point the CTO is fully in the strategy and business side of work. They will need to ensure they have the right set of reports and leads to ensure they are not disconnected from how the engineering team is performing.</p>
<p>This CTO will:</p>
<ul>
<li>Standardise the technology stack across multiple teams and establish technology governance frameworks for multiple teams/products.</li>
<li>Create centers of excellence for specialized technical areas.</li>
<li>Have an established set of contacts and relationships that they can call upon to help growth.</li>
<li>Have a deeper understanding of the competition and have expertise in what to look for during acquisition due diligence.</li>
<li>Manage technical integration of acquisitions.</li>
<li>Navigate regulatory requirements across different markets (data residency, compliance variations)</li>
</ul>
<h2>5. The Pre-Exit CTO</h2>
<p>At some point, the goal of many companies becomes to sell for big bucks through acquisition. In order to do this, the CTO will need to bulk up the areas which make the company attractive to buyers.</p>
<p>This CTO will:</p>
<ul>
<li>Articulate the technical vision and roadmap to potential acquirers.</li>
<li>Build relationships with technical leaders at potential acquiring companies.</li>
<li>Prepare comprehensive technical documentation for due diligence.</li>
<li>Ensure intellectual property is properly documented and protected.</li>
<li>Build redundancy in key technical roles to reduce "key person risk".</li>
<li>Demonstrate technical competitive advantages and moats.</li>
<li>Prepare for technical presentations to acquirer boards/leadership teams.</li>
<li>Ensure the technical team can operate independently during transition periods.</li>
</ul>
<h2>6. The Interim/Turnaround CTO</h2>
<p>No process is perfect - and sometimes a company will hit unexpected roadblocks or find themselves without a technical leader.</p>
<p>Some CTOs specialise in coming into struggling companies at any stage to fix technical debt, reorganize teams, or prepare for acquisition. These are often experienced technical leaders who thrive in high-pressure situations and can quickly assess what's broken.</p>
<p>This CTO will:</p>
<ul>
<li>Conduct rapid technical audits to identify critical issues and prioritize fixes.</li>
<li>Make tough decisions about legacy systems, technical debt, and underperforming team members.</li>
<li>Rebuild engineering culture and restore confidence in technical delivery.</li>
<li>Establish emergency processes to stabilize systems and prevent further outages.</li>
<li>Communicate transparently with stakeholders about technical realities and timelines.</li>
<li>Bring in specialised contractors or consultants to address skill gaps quickly.</li>
<li>Implement interim solutions while planning longer-term architectural improvements.</li>
<li>Often serve as a bridge leader while the company searches for a permanent CTO.</li>
<li>Balance urgent firefighting with building sustainable foundations for future growth.</li>
</ul>
<p>--</p>
<p>The CTO role is ultimately about matching technical leadership to business needs. Early-stage companies need hands-on builders; scaling companies need process architects; and expanding companies need strategic operators.</p>
<p>Transitions often hinge on inflection points such as raising a fund round, product market fit, the need for velocity or scale, or a re-org or acquisition. As a CTO growing with your company, proactive upskilling is key - learning to delegate and document, how to manage managers and how to embrace product and business metrics and goals - not just engineering ones.</p>
<p>When there's alignment between what the business requires and what the CTO is focused on, then the company will be primed for success. As a CTO, whether you evolve through all the stages or specialise in your sweet spot, success comes from being honest about where you add the most value - and where you don't. Being a great CTO isn't about holding the title. It's about recognizing what your company needs at its current stage - and whether you're the best person to lead it there.</p>
<p>What do you think about the stages listed here? Every company is different and some companies do activities in different orders. What is your experience with CTO roles across companies - is there anything you feel is missing?</p>]]></content:encoded>
      <pubDate>Fri, 13 Jun 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>5 Tools to Help with Vibe Coding Design</title>
      <link>https://www.andismith.com/blogs/2025/06/5-tools-to-help-with-vibe-coding-design</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/06/5-tools-to-help-with-vibe-coding-design</guid>
      <description>So you&apos;ve dipped your toes in to vibe coding and you&apos;ve coded a basic app, but it&apos;s lacking some flair. Here&apos;s some useful online tools that can help you make your design really vibe!</description>
      <content:encoded><![CDATA[<p>So you've dipped your toes in to <a href="/blogs/2025/03/vibe-coding">vibe coding</a> and you've coded a basic app, but it's lacking some flair. Here's some useful online tools that can help you make your design really vibe!</p>
<p>Three qualities about all the tools listed here:</p>
<ul>
<li>They are all online tools - no downloads needed</li>
<li>None require you to sign up to register</li>
<li>The functionality I mention is free (some are ad-supported, but it's not aggressive)</li>
</ul>
<h2>1. Coolors</h2>
<p><a href="https://coolors.co/">Coolors</a> has an excellent list of colour palettes to help give your web site or app a unique look. Simply find a palette set you like and hover over the colours to get the hex values - or export them to CSS variables or another format to use them all in your app.</p>
<p>Some of my favourite palettes include this <a href="https://coolors.co/palette/003049-d62828-f77f00-fcbf49-eae2b7">beach sunset combo</a> and this <a href="https://coolors.co/palette/264653-2a9d8f-e9c46a-f4a261-e76f51">blue-green-orange set</a></p>
<div class="post-image">
<img alt="Coolors" src="https://www.andismith.com/assets/blogs/2025/06/5-tools-to-help-with-vibe-coding-design/coolors.webp">
A great list of colour palettes
</div>
<h2>2. Squoosh.app</h2>
<p>Image resize and compression has never been so easy than with <a href="https://squoosh.app/">Squoosh.app</a>. As well as file uploads, Squoosh also supports pasting an image directly in - which I find extremely helpful when taking screenshots for this blog.</p>
<p>Squoosh runs its compression algorithms in the browser, so you don't send data to the server - and it can continue to process images when your connection is offline. It supports all the major image formats and has options for resizing, reducing colours and compression techniques directly in the app.</p>
<div class="post-image">
<img alt="Squoosh" src="https://www.andismith.com/assets/blogs/2025/06/5-tools-to-help-with-vibe-coding-design/squoosh.webp">
Image compression made easy
</div>
<h2>3. Lucide</h2>
<p>Getting a decent set of royalty-free SVG icons on the Internet can be a bit of a challenge, but <a href="https://lucide.dev/icons/">Lucide</a> has come to the rescue! As well as providing a comprehensive set of over 1,500 icons, Lucide has a React npm library which allows you to easily add SVG icons to your project, and ensure you only include the icons you use in your final app bundle size.</p>
<div class="post-image">
<img alt="Lucide" src="https://www.andismith.com/assets/blogs/2025/06/5-tools-to-help-with-vibe-coding-design/lucide.webp">
Lucide has over 1,500 icons!
</div>
<h2>4. SVGOMG</h2>
<p>Sometimes we can get the SVGs we need from an icon library and need to use a different source or make our own. <a href="https://optimize.svgomg.net/">SVGOMG's Optimizer</a> is great for cutting away unneeded instructions and data. You cna adjust the compression level with the in-app interface and see a real-time preview to check the quality of your SVG output.</p>
<h2>5. PhotoPea</h2>
<p>Editing an image or photo means a plethora of different tool options - and rarely are any of them as good as Adobe's original Photoshop. But Photoshop costs money, and this is a list of free tools! Enter <a href="https://www.photopea.com/">PhotoPea</a> - a Photoshop like experience available online without the expense, and without the requirement to sign up.</p>
<p>Photopea supports all the usual PhotoShop file formats, and can handle most of the actions and edits you would typically associate with PhotoShop.</p>
<div class="post-image">
<img alt="Photopea" src="https://www.andismith.com/assets/blogs/2025/06/5-tools-to-help-with-vibe-coding-design/photopea.webp">
A Photoshop-like experience
</div>
<p>So there you go - five of my favourites. Let me know if you have any excellent recommendations to add to the list!</p>]]></content:encoded>
      <pubDate>Tue, 10 Jun 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Buy vs. Build</title>
      <link>https://www.andismith.com/blogs/2025/05/buy-vs-build</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/05/buy-vs-build</guid>
      <description>When you&apos;re building a product at a startup, every engineering hour counts. You can&apos;t build everything from scratch, but you also can&apos;t buy your way out of every problem. The key is knowing where to draw the line.</description>
      <content:encoded><![CDATA[<p><strong>When you're creating a new product at a startup, every engineering hour counts. You can't build everything from scratch, but you also can't buy your way out of every problem. The key is knowing where to draw the line.</strong></p>
<p>My approach in this scenario is simple: build what makes you unique, buy everything else.</p>
<p>If a feature was core to our competitive advantage - the thing that would make customers choose us over competitors - we built it. For functionality where solutions already existed or where we were dealing with table stakes
functionality that users take for granted such as login, we looked for existing solutions.</p>
<h2>Making the Decision</h2>
<p>If there is any doubt on what to build vs. buy, it's always worth looking back to the objectives of the business and considering how this decision would affect them. It was far more important to get our product out in the
market and being used by our customers, so we made choices that reflected that.</p>
<p>Buying software comes with its own complications. Often there are multiple tools that can help you achieve your goals. Some lightweight SWOT analysis can help you get a handle on what's needed, but it's also worth looking
at what your team have used before and factoring that in to the choice made. Why spend weeks learning a new authentication system when we could integrate Auth0 in a day?</p>
<p>The biggest trap with decision making can be analysis paralysis. In start up world, time is everything - so sometimes you just have to make your best guess with the information you have and move forward.</p>
<h2>When It Doesn't Work Out</h2>
<p>Not every decision can be perfect. We once chose a third-party service that seemed ideal but later became a problem as we learnt more about how our product was being used. Something that seemed fit for purpose during our
SWOT analysis became a bottleneck for our team.</p>
<p>With hindsight, we could have done further testing in this area - but our product was also still evolving in to it's current form and we were still learning how our users would interact with the product so it's difficult
to accurately surmise that we would have found the problem.</p>
<p>As your startup and product grows, and you understand more about how your users are using your product, you can start to unpick some of those initial choices that didn't work out.</p>
<h2>The Real Win</h2>
<p>Taking a Buy vs. Build mentality on each software decision worked really well for us. The biggest benefit wasn't just time saved - it was focus gained. By outsourcing the mundane stuff, our team could obsess over the features
that actually mattered to users. We became experts in our domain instead of generalists trying to solve every problem.</p>
<p>Start with what makes you different. Everything else can probably be bought.</p>]]></content:encoded>
      <pubDate>Sat, 31 May 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>AI to Solve Actual Product Problems</title>
      <link>https://www.andismith.com/blogs/2025/05/ai-to-solve-actual-product-problems</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/05/ai-to-solve-actual-product-problems</guid>
      <description>AI is improving by leaps and bounds week after week. Everyone’s trying to bolt AI onto their product. But AI only adds value when it’s solving the right problem.</description>
      <content:encoded><![CDATA[<p><strong>We live in exciting times.</strong></p>
<p>AI is improving by leaps and bounds week after week. Everyone’s trying to bolt AI onto their product. But AI only adds value when it’s solving the right problem.</p>
<p>One way I've explored using GenAI is offering a scalable, inexpensive conversational experience - the kind that could support engaging advisor-like interactions.</p>
<p>To test this I quickly prototyped a ChatGPT-based experience with an elaborate prompt. What became apparent was that while the AI could generate responses, the quality was inconsistent, and - more importantly - users were hesitant to open up in a computer-mediated interaction. AI lacks the genuine feeling of empathy you find talking with a human.</p>
<p>The truth is - AI isn't a magic bullet.</p>
<h2>Time for a Rethink</h2>
<p>It became clear that while AI had potential, I hadn’t yet aligned its capabilities with a well-defined user need.</p>
<p>Once I revisited the original problem I was trying to solve - offering a superior service to our users - it was clear the initial direction wasn’t fully aligned with the problem. The breakthrough came when I started asking 'what does our team actually need to offer a superior service?' instead of 'what can AI do?'</p>
<p>Rather than AI replacing the capabilities within the team, we pivoted toward using AI to amplify our teams' capabilities instead. Our goal became to equip them with superpowers.</p>
<p>Conversations with users would still be human-to-human, but supported by Natural Language Processing, integrated services, and content resources.</p>
<p>A huge number of different approaches become available when you start thinking about how you can give superpowers to your team. In thinking through potential applications, I focused on ideas that balanced feasibility, usefulness to advisors, and user trust. Possible approaches could include:</p>
<ul>
<li>Summarising conversation history to reduce the prep time before a session.</li>
<li>Automated note-taking during conversations.</li>
<li>Surface real-time sentiment cues to support advisor awareness.</li>
<li>Suggest relevant exercises and draw from anonymized insights or content.</li>
<li>Progress tracking and visualisation across multiple sessions.</li>
<li>Automate scheduling and follow-up reminders based on the conversation outcomes.</li>
</ul>
<p>To prioritise and understand how we should approach it, we considered how it was adding business value and the feasibility of building and supporting it.</p>
<h2>A Better AI Product</h2>
<p>It probably won't strike you as a surprise that taking a problem-first approach for AI led to far better user satisfaction and higher engagement.</p>
<p>As tempting as it is to go all-in on AI and be blinded by the latest and greatest release, be sure to:</p>
<ul>
<li>Quickly prototype ideas and test things out.</li>
<li>Be willing to fail and pivot.</li>
<li>Remember the reason your product exists in the first place.</li>
</ul>
<p>With these things in mind, you'll create a better problem-first AI experience.</p>
<p>Have you tried to integrate AI into a human-first workflow? I'd love to hear what worked for you.</p>]]></content:encoded>
      <pubDate>Sun, 25 May 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Yes And...</title>
      <link>https://www.andismith.com/blogs/2025/05/yes-and</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/05/yes-and</guid>
      <description>How Improv taught me to be a better leader.</description>
      <content:encoded><![CDATA[<p><strong>How Improv taught me to be a better leader.</strong></p>
<p>On Friday night, I performed an improv comedy murder mystery at the Brighton Fringe as part of the troupe "Who Killed The Plot". We played to a sold out audience who had paid £8 each to be there. The audience really vibed with our comedy and the show went better than any of us could have expected.</p>
<div class="post-image">
<img alt="On stage at the Brighton Fringe" src="https://www.andismith.com/assets/blogs/2025/05/yes-and/fringe.webp">
On stage at the Brighton Fringe
</div>
<p>I've had an interesting journey to get to this point. Two years ago, I had never done any improvised speaking before. I never did drama or music at school. I had been on stage to deliver multiple rehearsed technical talks - but the thought of being on stage without the safety of a script or a powerpoint deck terrified me. Now I'm quite happy to get on stage to speak, act, sing an improvised song or even do some freestyle rap.</p>
<p>I originally started going to improv to improve my confidence when speaking to investors. I wanted to feel confident when I was talking about what we were doing at Bloom without needing to stick to a script. I found an improv community near where I live called <a href="https://www.dingbatsimprov.com/">Dingbats Improv</a> and decided to go along. From the very first workshop I realised I had found something that was fun, a great way to remove any stress from the day and completely different from anything else I'd ever done.</p>
<p>As well as being a lot of fun, Improv has taught me many life skills and ways to be a better leader, a better partner and a better person:</p>
<h3>Yes And</h3>
<p>"Yes And" means accepting your scene partners offer and building upon it. You wanted your scene partner to feel supported, and you want to make them look as good as possible on stage.</p>
<p>As a leader, this same technique applies to building a strong team. When the team have suggestions it's important to value their contribution and then expand or redirect, rather than shutting them down. This creates a culture where people feel heard and have psychological safety - which results in more creative problem solving and more willingness to share innovative ideas.</p>
<p>For example, if an engineer suggests a change in process or architecture it may not be the right timing now, but there may be an element that you can start incorporating early on and schedule time to review the remainder at a later date. This will preserve their enthusiasm while acknowledging the current constraints. Later on, when you do the comprehensive review, they will be even more engaged because they saw that their input genuinely mattered.</p>
<h3>Confidence</h3>
<p>Improv requires performers to make bold choices without hesitation. If you wait too long to make a choice, the moment has passed and you have missed the laugh. Improv teaches you to trust your instincts and fully commit to the decisions you make.</p>
<p>In leadership, it's very easy to put off a decision or end up in analysis paralysis. Without hindsight, it's often impossible to know what is the right choice - we are just left with best guesses. We have to instill confidence in our team to take chances with decisions and.. embrace failure.</p>
<h3>Embrace Failure</h3>
<p>In Improv, mistakes are often celebrated as opportunities. Often the funniest laughs come from when things go wrong - like when someone badly mimes an action or something unexpected happens.</p>
<p>In technology, things also go wrong from time to time, and while it's not funny when it happens in the world of work, having the ability to remain calm and reframe failures is crucial. Think through how a failure can become an opportunity to make something greater.</p>
<p>Creating a team environment where people aren't afraid to fail encourages innovation and experimentation. When team members know that an unsuccessful attempt won't be met with blame but rather with curiosity and support, they're more willing to propose bold ideas and take calculated risks. Just make sure you are learning from your failures and building those lessons into your process.</p>
<h3>Active Listening</h3>
<p>Improv scenes rely on truly listening to your scene partners rather than waiting to speak. In leadership, this means giving full attention to the team and the problems you are trying to fix. Being present in conversations, asking clarifying questions and responding to what's happening rather than sticking to the path you originally thought was best. Active listening will help you build stronger relationships with your team and lead to making better decisions.</p>
<h3>Reading the Room</h3>
<p>As an improviser, you need to have awareness of everything that is going on on-stage. Having an understanding of group dynamics and your teams' emotional states is also an important skill of being a leader.</p>
<p>Quite often when we make a decision as a group we may have a developer or designer who says they are ok, but actually they are unhappy with the decision that has been made. Perhaps they have some concerns with the approach, or a better understanding of how to be successful, or maybe they don't believe in the idea yet. Being able to sense those moments will allow you to hone in on these situations and ultimately lead to better team dynamics and leadership.</p>
<h3>Storytelling</h3>
<p>Having to make stories up all the time on stage lends itself well to being able to tell narratives in the work place. It's helped me develop my skills in communicating my vision and explain the story about the technology solutions we are building. It's changed how I can communicate with non-technical stakeholders and how I can inspire others.</p>
<p>Hopefully this article has given you some insights in to how Improv has helped me improve, and how these ideas relate to being a better leader. Maybe it has sparked your curiosity in to trying some of these techniques yourself. There are many improv workshops and classes out there, just a Google away. Good luck!</p>
<p>And if you are already in an Improv group - <a href="/games/improv-suggestions">try my improv suggestion generator</a>!</p>]]></content:encoded>
      <pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Remote-First Engineering at Scale</title>
      <link>https://www.andismith.com/blogs/2025/05/remote-first-engineering-at-scale</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/05/remote-first-engineering-at-scale</guid>
      <description>The pandemic in 2020 changed the world of software development forever, with teams forced to work remotely.</description>
      <content:encoded><![CDATA[<p>The pandemic in 2020 changed the world of software development forever, with teams forced to work remotely.</p>
<p>At LifeWorks, when the pandemic hit we were in the middle of delivering a large and complex multi-team architecture change which was closely tied to a client obligation. The financial implications of missing our deadline were too high. We needed to make remote working work.</p>
<p>More recently teams have been moving back toward office working - usually in a hybrid situation. There is certainly something for face-to-face time and getting to know your team, and in the early stages of a startup where time is critical being able to make quick decisions.</p>
<h2>The Remote-First Mindset</h2>
<p>Having a large team working remotely means you need to pay far closer attention to the ways of working and the culture of the team. It's very easy for someone to feel disconnected from the business decisions that may affect them.</p>
<h3>Documentation is king</h3>
<p>I'm a big evangelist for having great documentation. New starters should feel supported from the start, and everyone should know where or who to go to if they need help or support. Taking extra time to make sure your systems are explained can help save a lot of time later on.</p>
<ul>
<li>Keep a decision log which documents the choices that have been made whilst building your system. This can help future team mates understand the <em>why</em> a architectural or technical choice was made, and it also gives an opportunity to document what the potential technical debt issues would be with such a choice.</li>
<li>Ensure your onboarding documentation is clear and guides team mates on how to set up their systems to get up and running quickly.</li>
<li>Keep an easy to access document on table stakes knowledge - such as how your servers are configured, what the responsibilities are of each service or repository and keep high level architecture diagrams and technical roadmap decisions.</li>
<li>Keep a glossary of terms that are internal to your business or specialist knowledge for your subject domain.</li>
</ul>
<h3>Async</h3>
<p>Asynchronous communication is where one team member asks for, or provides some information, and then there is a time lag before the recipients take in the information and offer their responses. Usual examples of this are Slack or email.</p>
<p>As a designer, developer or other do-er there are lots of benefits to this approach - you can plan your day to ensure you have good chunks of focus time and pick up correspondence at set intervals.</p>
<p>Asynchronous video is also another great tool for communicating more effectively with your team. Sometimes our messages get misunderstood in text, but we can clearly convey what we want and map our words to pictures with a video. Your team mates can find a time that works well for them to watch it.</p>
<p>In the development world, we also found having an agreed time where pull requests would be reviewed helped us ensure work did not get blocked and set expectations around how quickly code changes would happen.</p>
<h3>Managing Time Zones</h3>
<p>Timezones (and by extension country-specific bank holidays) can add an extra complication to the way you manage remote teams.</p>
<p>At LifeWorks, most of my 100+ engineering team were located in the UK or Europe and we worked in way where hours were adjusted around a set of base times (between 10am - 4pm UTC). We agreed that during the base times we would run standups and any important meetings so everyone could attend.</p>
<p>For managers and developers that are outside of Europe, there were additional challenges - async communication massively helped here and aligning check-in meetings to a time where you both were working was important. Having team members working completely different hours can actually be really useful if you have a time-sensitive issue as you can have a team working on the issue round the clock to help with a resolution, and this certainly helped with some of our incident availability issues. For example, if an issue occurred late in the day in the UK, our US, Canadian and Australian counterparts could continue the investigation with the UK/Europe team remaining on-call.</p>
<h3>Trust</h3>
<p>A remote-first culture requires a level of trust among employees. You have to trust your team to be able to plan out their time and be accountable for their outputs. For us, this meant weekly sprint commitments that engineers owned completely. We measured progress by completed work, not hours logged. When someone consistently under-delivered, we had specific conversations about planning accuracy and communicating issues early rather than questioning their work ethic. As a leader, you can guide and set expectations of what this should be and you need to be transparent and prompt with letting them know when your expectations are not being met.</p>
<h3>Keeping Connected</h3>
<p>It seems like a really trivial and simple thing, but at both LifeWorks and Bloom I set a tradition of saying "good morning" on an Engineering Slack channel when we first logged on for the day. It meant that every day, people had at least some connection with their team and kept small talk alive. It helped to remove a sense of loneliness or isolation.</p>
<p>I also ran a couple of sessions to help with team building - we used a tool called <a href="https://www.brightful.me/play/">Brightful</a> to organise fortnightly Friday fun, and I had a drop-in "ask me anything" session where people could drop in and chat about questions or problems they were having.</p>
<h3>1-1s</h3>
<p>I've <a href="/blogs/2024/11/lets-talk-about-1-1s">written about 1-1s before</a> so I won't go in to much detail here - other than to say they are critical in a remote-first environment.</p>
<h3>Outsourcing</h3>
<p>With outsourcing, the biggest mistake I see is treating offshore teams like an external vendor instead of integral team members. At LifeWorks, we used a number of outsourcing partners to help bolster our team and deliver our roadmap. For me personally, I wanted to ensure that outsourced developers were treated like full-time employees. We worked with our partners to set expectations of the skills and experience we were looking for and we interviewed our outsourced developers with the same standards we used for internal hires. As we'd hired competent outsourced developers, we ensured they were also part of our decision making processes and felt included in our sprint rituals. Working with a team that is fully remote actually made this easier as with everyone on a video call the playing field is even.</p>
<h2>Conclusion</h2>
<p>What started as a pandemic necessity became a competitive advantage - and these principles helped us not just survive that deadline, but grow and scale successfully through acquisition. There's a lot to talk about with remote-first working but this hopefully gives you an idea of how you can make it successful.</p>]]></content:encoded>
      <pubDate>Wed, 07 May 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Improv Suggestions</title>
      <link>https://www.andismith.com/blogs/2025/05/improv-suggestions</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/05/improv-suggestions</guid>
      <description>Sometimes when you are rehearsing or practicing Improv, getting a varied starting word can be the hardest thing.</description>
      <content:encoded><![CDATA[<p><strong>Sometimes when you are rehearsing or practicing Improv, getting a varied starting word can be the hardest thing.</strong></p>
<p>I know I often fall back to the same words and suggestions as they come to my mind first time and time again.</p>
<p>Inspired by a rehearsal I did recently, I've put together a little <a href="/games/improv-suggestions">Improv Suggestion Generator</a> that can help you with ideas for your next rehearsal.</p>
<p>I hope you find it useful, and <a href="https://www.linkedin.com/in/andismith/">drop me a message</a> if you have any sugggestions for how I can make it better!</p>
<p><a href="/games/improv-suggestions">Access it here</a>!</p>]]></content:encoded>
      <pubDate>Sun, 04 May 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Vibe Coding</title>
      <link>https://www.andismith.com/blogs/2025/03/vibe-coding</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/03/vibe-coding</guid>
      <description>What is vibe coding? Should we all be vibe coding? And how can we do it right?</description>
      <content:encoded><![CDATA[<p><strong>What is vibe coding? Should we all be vibe coding? And how can we do it right?</strong></p>
<p>Coding with AI is moving at a fast pace thanks to great tools like <a href="/blogs/2025/03/getting-the-best-out-of-cursor-ai">Cursor</a>, Highlight, Windsurf and others.</p>
<p>The Cursor team have been working super hard on pushing lots of updates to their IDE and it's made the possibility of vibe coding much easier for everyone to try.</p>
<h2>What is Vibe Coding?</h2>
<p>Coined by OpenAI co-founder Andrej Karpathy in February 2025, <strong>Vibe coding</strong> is an AI-dependent programming technique where a person describes a problem in a few sentences as a prompt to a AI chatbot or tool like Cursor and the AI generates all the code, shifting the human's role from manual coding to guiding the AI to generated source code, often without understanding what is being written.</p>
<p>This relates back to Karpathy's 2023 statement that "the new hottest programming language is English".</p>
<h2>Why vibe coding is cool</h2>
<p>Vibe Coding allows anyone to produce applications without having to think about the complexities of code. They can reel off an idea to an AI and then iterate on that idea without having to write any lines of code. And the results can be pretty impressive.</p>
<h2>Why doesn't everyone just vibe code then?</h2>
<p>For a non-engineer, vibe coding is a great gateway to getting started with code. It's really easy to get something going and make quick progress.</p>
<p>For an engineer, however the situation is slightly different.</p>
<p>Non-engineers see vibe coding and then struggle to understand why engineers are still taking time to write code manually.</p>
<p>There are a few reasons for this:</p>
<h3>1. It's far easier to write code than it is to read code</h3>
<p>When developers write code, they try to follow best practices for keeping code clean and maintainable through consistent naming, DRY principles and architected modular design. Writing readable code is a very hard thing to do - and even when coding manually developers often don't get it exactly right.</p>
<p>Vibe coding is a bit like having a junior developer on your team - they do exactly what you ask for without thinking about the bigger picture; or even if they should do it; and they have a habit of going off track and making other changes too.</p>
<h3>2. The 80:20 rule</h3>
<p>If you rely solely on vibe coding without establishing proper guardrails first (which may happen if you don't understand what guardrails are needed), you'll inevitably encounter problems as your application grows in complexity.</p>
<p>This situation illustrates the 80:20 rule of software development - where the final 20% of a project often requires the most time and effort to complete properly. With vibe coding, the challenge is compounded because you lack familiarity with the initial 80% of the codebase that was AI-generated.</p>
<p>As you approach the final stages of product development and begin refining your concept, issues are more likely to emerge. At this point, the vibe code engineer often finds themselves stuck.</p>
<p>For non-engineers, resolving these issues becomes nearly impossible because they don't understand the underlying code structure or problems. Even for experienced engineers who can read code, they're confronted with a disorganized collection of code that doesn't follow best practices, resulting in significantly longer debugging sessions.</p>
<h3>3. What about security?</h3>
<p>When vibe coding, you might overlook critical security considerations that professional developers would routinely implement. Essential security practices such as:</p>
<ul>
<li>Ensuring user inputs are safe</li>
<li>Passwords are not stored in plain text</li>
<li>API and Database keys are stored securely</li>
<li>APIs are rate limited</li>
<li>Permissions are set up correctly</li>
<li>Errors are handled carefully</li>
</ul>
<p>Not considering these security issues at all leads to issues like the ones we are seeing in the AI community now.</p>
<div class="post-image">
<img alt="When it all goes wrong" src="https://www.andismith.com/assets/blogs/2025/03/vibe-coding/under-attack.webp">
When it all goes wrong
</div>
<h2>So what can we do?</h2>
<p>Engineers naturally approach AI code generation with caution. Beyond concerns about being undervalued or replaced, they prioritize keeping code readable and maintainable over time. While Cursor's MDC files can help address some of these concerns, you still need technical expertise to use them effectively.</p>
<p>Fortunately, this isn't an all-or-nothing proposition. Many routine tasks can be automated while engineers maintain control over the codebase structure. Time-consuming but low-value tasks like test maintenance and linting fixes can be efficiently handled using Cursor's YOLO mode, freeing up valuable time for more strategic coding work. I explore these automation opportunities in more depth in <a href="/blogs/2025/03/getting-the-best-out-of-cursor-ai">my Cursor tips and tricks article</a>.</p>
<p>For non-engineers excited about vibe coding, keep the 80:20 rule in mind: what seems easier at the beginning often becomes significantly more challenging later in the development process.</p>
<h2>Tips for better Vibe Coding</h2>
<p>From my experience, the best use case for vibe coding is to test the vibe of an idea. As per <a href="https://x.com/levelsio">Pieter Levels</a> recommendation - creating a prototype in 3 hours to see if the idea is even feasible is a great use case for vibe coding. It produces a lot of thought about the use cases of the idea and information about how it would work.</p>
<p>Here's some tips for being better at vibe coding:</p>
<ol>
<li>Use AI to first make a <a href="https://www.productplan.com/glossary/product-requirements-document/">PRD (Product Requirements Document)</a> before writing code. Include the PRD file in your codebase. Grok, o1 Pro or GPT 4.5 are particularly good at creating these.</li>
<li>Be less specific. Vibe coding is about getting things "almost right" and accepting compromises. AI has a habit of getting stuck in the most unusual situations.</li>
<li>Vibe coding frequently won't go right. So start over when things go wrong. Learn lessons with each iteration on what did and didn't work with your prompts until you get something good. Cursor also has a handy 'revert' feature that allows you to step back through code changes.</li>
<li>Utilise TDD to get the AI to test it's own code, and ask it when you want it to break code up in to smaller pieces.</li>
</ol>
<p>As of March 2025, for production code of complex applications however, you're probably best keeping it in the safe hands of your engineers.</p>
<p>As I wrote at the top of the article, coding in AI is moving fast - so let's see how long it takes for that to change.</p>]]></content:encoded>
      <pubDate>Wed, 19 Mar 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Generative AI to boost Content Workflows and SEO</title>
      <link>https://www.andismith.com/blogs/2025/03/generative-ai-to-boost-content-workflows-and-seo</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/03/generative-ai-to-boost-content-workflows-and-seo</guid>
      <description>Recently I have been experimenting with how AI can help improve content workflows and SEO through the use of real-time content generation.</description>
      <content:encoded><![CDATA[<p>Recently I have been experimenting with how AI can help improve content workflows and SEO through the use of real-time content generation in a automated content workflow system.</p>
<p>The real world applications for this would include knowledgebases; help and support centres and SaaS product search bars. Companies can create knowledgebases of their systems and products without requiring mass manual creation of content - they simply handle curation.</p>
<h2>How it works</h2>
<p>The system works as follows:</p>
<ul>
<li>The user visits the site or product on a subject matter they are interested in</li>
<li>There is a prominent search bar encouraging the user to find what they are looking for which they use to search for content</li>
<li>If the content exists, it is directly served to the user</li>
<li>If the content does not exist, then we use AI coupled with Retrieval Augmented Generation to generate the content on the fly</li>
<li>This content is immediately presented to the user who searched for it.</li>
<li>The user can mark the content as useful if it is appropriate - which will then trigger it to be published and added to the site and site map for SEO.</li>
</ul>
<p>With this approach, we no longer turn a user away when they don't find something relevant - we instead offer a way for them to find</p>
<h2>Checks and balance</h2>
<p>Of course, with any content being generated automatically we need checks and moderation. There's two areas we need to be careful of here:</p>
<ul>
<li><strong>User input</strong> - Is this input a valid input? We don't just need to check for bad language - we also need to check it's relevant to the subject matter of our website (and not sending users to our competitors).</li>
<li><strong>AI output</strong> - Is the output from the AI correct and relevant, or has it been hallucinating?</li>
</ul>
<p>We can use a set of AI and non-AI checks to ensure our inputs and outputs are correct.</p>
<p><strong>Input</strong></p>
<p>Before anything happens, we check to see if the content already exists in our system using the regular search techniques.</p>
<p>If the content doesn't exist, then we do some rundementary checks - is the query of a minimum viable length (or not overly wordy) and does it pass a bad words filter.</p>
<p>Next a cheaper AI model (in my case Claude Haiku) handles our initial screening - verifying whether the search term is relevant to our subject matter.</p>
<p>Once we are happy, we can send it through to our more expensive AI model (Claude Sonnect) together with any relevant content from our RAG (Retrieval Augmented Generation) database to create our new content.</p>
<p><strong>Output</strong></p>
<p>For the output, we specify a JSON structure we would like the AI to return our content, and then we run a secondary check with another AI prompt to ensure that this secondary model agrees the content is relevant to our subject matter.</p>
<p>This content is then surfaced back to the user to help with their query, but it remains unpublished on the site.</p>
<p>A secondary threshold (did the user find it useful, is it a common search term) determines if the content becomes a published article on the site.</p>
<h2>Putting it all together</h2>
<div class="post-image">
<a href="/assets/blogs/2025/03/generative-ai-to-boost-content-workflows-and-seo/diagram.webp" target="_blank"><img alt="Content generation process" src="https://www.andismith.com/assets/blogs/2025/03/generative-ai-to-boost-content-workflows-and-seo/diagram.webp"></a>
The content generation process
</div>
<p>The diagram above illustrates how it all fits together and how AI components (marked in purple) are integrated with traditional content management processes (in white) and decision points (in green) to create a comprehensive content workflow system.</p>
<h2>A working prototype</h2>
<p>I have built a working prototype of this system as a directory for technical terms.</p>
<div class="post-image">
<img alt="Screenshot of a knowledgebase for technical terms" src="https://www.andismith.com/assets/blogs/2025/03/generative-ai-to-boost-content-workflows-and-seo/tech-terms.webp">
A knowledgebase for technical terms
</div>
<p>The prototype behaves as follows:</p>
<ul>
<li>it is connected to a content database filled with technical terms</li>
<li>if the user searches and a term isn't found, then the content is generated and served to the user</li>
<li>an alert is triggered to notify me for review and if I approve it, the content will be published on the site</li>
</ul>
<p>If you'd like to explore more about how this system works and how we could partner in developing this or similar systems, <a href="https://www.linkedin.com/in/andismith/">please contact me on LinkedIn</a>.</p>]]></content:encoded>
      <pubDate>Tue, 18 Mar 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Getting the best out of Cursor AI</title>
      <link>https://www.andismith.com/blogs/2025/03/getting-the-best-out-of-cursor-ai</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/03/getting-the-best-out-of-cursor-ai</guid>
      <description>AI is rapidly changing the way how we code. Cursor is one particular tool which has changed the game. Here&apos;s some tips on how to get the best out of it.</description>
      <content:encoded><![CDATA[<p><strong>AI is rapidly changing the way how we code. Cursor is one particular tool which has changed the game. Here's some tips on how to get the best out of it.</strong></p>
<p>I've been using <a href="https://www.cursor.com/">Cursor</a> daily for some time now and it's been a interesting learning experience. Sometimes it feels like my greatest co-worker - it's reading my mind, it's saving me a heap of time; and then suddenly it turns on me and it starts to produce convulated, broken solutions to what should be a simple problem, or it creates a bunch of new components rather than use what we already have.</p>
<h2>Be explicit</h2>
<p>The general rule is: the more precise you are with your instructions to Cursor, the better the output. Cursor isn't a mind reader, it needs context if you want it to perform in a particular way.</p>
<p>For example, if you know it is going to need to call a particular API or function, let it know by @ the relevant file. This will help avoid the danger of it creating repetition in your code base.</p>
<p>Also make sure your prompt doesn't ask too many things - Cursor is much better at implementing functionality in smaller pieces. Plus with smaller pieces, you are likely to be more precise with your ask.</p>
<h2>Making better prompts</h2>
<p>Regardless of how clear you make your prompt, it's unlikely AI is going to be on exactly the same wavelength as you and it's unlikely it will get everything right on it's first go.</p>
<p>Test Driven Development (TDD) can help you get a better handle on this. The TDD cycle follows three primary steps:</p>
<ul>
<li>Write a failing test that defines the functionality you want to implement.</li>
<li>Write the minimal amount of code to make the test pass.</li>
<li>Clean up and optimize the code while ensuring tests still pass.</li>
</ul>
<p>To get Cursor to do this for us we can suffix our build AI prompt with an instruction to write our tests before writing the code. Furthermore, if we enable and configure YOLO mode in Cursor's <code>Settings > Cursor Settings > Features</code>, we can also give it permission to run <code>npm test</code> which means it can run our tests iand iterate on the code until they all tests pass. We can also use this method to specify any test edge cases we need the AI to consider.</p>
<h2>Get Cursor to think it through</h2>
<p>Cursor's chat box has both 'Agent' and 'Ask' functionality. While the 'Agent' is useful for most things, if Cursor isn't giving you the results you want, then getting it to think about the problem in the 'Ask'. Ask for three different solutions to the problem, and get Cursor to discuss the pros and cons for each. Then you can get it to build the solution you think will work best.</p>
<h2>Try something else</h2>
<p>Sometimes Cursor just can't fix a problem. If it's tried two or three times and it's not finding a suitable resolution, then it's likely you need to change something about the ask. There's three ways we can change this:</p>
<ul>
<li>present the problem differently</li>
<li>try another tool</li>
<li>do it ourselves</li>
</ul>
<p>I've actually found a lot of success with taking the problem statement away from the contextbuilt up in Cursor and instead using a fresh Claude or OpenAI chat window. Talking to an AI that isn't living your app can give a secondary perspective to understand the problem which then will either help you come to a better solution, or it will help you chat to Cursor in a way it finds easier to understand.</p>
<h2>Try a different model</h2>
<p>Speaking of using something else, there is a lot of value in trying a different model within the Cursor IDE itself.</p>
<p>Currently using the new Claude 3.7 agent in particular seems to cause a lot of extra unwanted activity and change. For example, it may add the requested functionality to a file and then decide it will go on a refactoring journey across the codebase whilst you're frantically looking for the 'stop' button.</p>
<p>You can swap models within Cursor whenever you like - simply hit the dropdown next to Agent (which has the model name or 'default'); and try again with another model. You'll find some AI models are better for planning, some are better for coding, some are better for debugging.</p>
<h2>Create rules</h2>
<p>Previously we'd create a <code>.cursorrules</code> file in our root directory, but Cursor has recently moved to allowing multiple rules through a new MDC format hosted in <code>/.cursor/rules</code>. With MDC, you can break up your rules in to separate files and then apply it to specific files or directories using globs. For example, if I wanted to apply a set of rules only to React components, I could create a file like:</p>
<pre><code>---
description: Generating React Components
globs: *.tsx, *.jsx
---
# Coding style guide
- Style guide goes here
</code></pre>
<p>As well as style guides you can also add other information to assist Cursor around your code base - for example, consider adding information on the following:</p>
<ul>
<li><strong>App Flow</strong> - how users navigate through your app</li>
<li><strong>Backend</strong> - the structure of the backend and any techniques you want to consistently use</li>
<li><strong>Frontend</strong> - the structure of front end code</li>
<li><strong>Requirements</strong> - how the app should behave</li>
<li><strong>Implementation plan</strong> - a numbered implementation plan, broken down in to well defined, small steps so the AI can concentrate on one thing at a time</li>
</ul>
<p>You can get AI to help you create these through conversation AI such as Claude, though make sure you review the documents manually too.</p>
<h2>Commit often, review your commits</h2>
<p>Though it is possible to rollback to previous versions with Cursor, it's quite easy to get lost within lots of untracked changes. Having a sensible git commit history allows you to quickly revert if something goes badly wrong.</p>
<p>You can commit changes from within Cursor's source control panel and then @ tag your Git commit within Cursor's Ask chat prompt to ask for improvements and potential bugs.</p>
<h2>Will Cursor replace me?</h2>
<p>While it's undeniable that the speed of progression with AI coding has been phenomenal, the requirement for real developers is unlikely to go away in, at least in the short term. The advancements in code quality that AI is making are starting to reach diminishing returns and the solutions built with AI tend to ignore the sensibilities of writing good code.</p>
<p>Developers are likely to take more of an orchestrator role with AI ensuring that the code that is written is of good quality and fit for purpose. Remember, code readability is far more important than how quickly we write, so don't blindly accept code - make sure it is something you will understand when the context of the chat window is long past and your debugging an issue in production.</p>
<p>Good luck out there!</p>]]></content:encoded>
      <pubDate>Wed, 12 Mar 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Containment DX</title>
      <link>https://www.andismith.com/blogs/2025/03/containment-dx</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/03/containment-dx</guid>
      <description>One of the earliest games we released at Natomic Studios was a 360 Pong variant called &apos;Containment&apos;. It&apos;s been lost to time thanks to the advancement of technology, so I thought I&apos;d have a go at rebuilding it on the web using AI.</description>
      <content:encoded><![CDATA[<p><strong>One of the earliest games we released at Natomic Studios was a 360 Pong variant called "Containment". It's been lost to time thanks to the advancement of technology, so I thought I'd have a go at rebuilding it on the web using AI.</strong></p>
<blockquote>
<p><a href="/games/containment/index.html">Play Containment DX in your web browser</a>. Best played in Chrome on desktop.</p>
</blockquote>
<p>The original "Containment" game was created by Rich Hodgson and had quite a following on the high scoreboards on Natomic's website. In the game, the player controls both paddles and has to stop the ball from escaping the boundaries of the circle. As time progresses, the ball gets faster adding extra complexity.</p>
<p>You can <a href="https://www.youtube.com/watch?v=YUZAPzBXko0">watch a playthrough on YouTube</a>. As you can see, the gameplay is fairly straightforward - the times were simplier back then!</p>
<h2>Rebuilding in JavaScript with Generative AI</h2>
<p>I thought it'd be an interesting challenge to see if we could easily replicate this game on the web with help from Claude and Cursor. For this project, my approach was to use Claude's web interface to build an initial version and then move over to Cursor when I needed to start editing the code. I then wanted to use AI to produce extra visual effects, audio and music.</p>
<p>Earlier in the day, I'd been trying to replicate a LucasArts adventure game with AI which had proved too much of a challenge to overcome in a few hours - but with this much simpler game and the assistance of Claude AI, it was very easy to get a simple remake of the core game concept working.</p>
<p>Once the initial gameplay was working, I then used Cursor AI to help me improve the original game with power-ups and particle effects, and other AI tools to assist with music, voice overs and sound effects. This article breaks down my journey building this game with AI.</p>
<h3>Initial Prompt</h3>
<p>After playing and reviewing the original game, I settled on the following prompt for Claude Sonnet 3.7:</p>
<pre><code>I want to make a special version of Pong in JavaScript and Canvas 2D where the player controls both paddles and they rotate 360 degrees around a circle.

The paddles are controlled with the left and right cursor keys.

The ball needs to be contained within the circle and should bounce when colliding with the paddles. Each bounce should add 100 points to the users score. As the ball gets faster, a multiplier should be added to the score.

As time progresses, the ball should gradually get faster. (every 15 seconds)

The player starts with three lives. If the ball leaves the play area, the player loses a life.

When they have lost all their lives, they are presented with a game over screen and their score.

Before starting the game, there should be a title screen with the word "Containment" and "Press Any Key to Start" on it.
</code></pre>
<p>This got me started with an initial version of the game with circular paddles and a ball that bounced around the screen. I then needed to provide AI instructions for some adjustments to the initial gameplay - e.g. slow down the ball as it's unplayable!</p>
<p>I chose to use Canvas 2D as it allows me to manipulate the entire screen like a bitmap, and is perfectly suitable for a 2D game.</p>
<h3>Drawing and Animating the 360° Circular Paddles</h3>
<p>The game uses a circular arena with paddles that rotate around its perimeter. Let's take a look at how this is implemented:</p>
<p><strong>Paddle Initialization</strong></p>
<p>The arena is positioned at the center of the canvas, with a radius that ensures it fits within the canvas boundaries, and the paddles are initialised to fit in that.</p>
<pre><code class="language-javascript">function initPaddles() {
  paddles = [];
  const angleStep = (2 * Math.PI) / paddleCount;

  for (let i = 0; i &#x3C; paddleCount; i++) {
    paddles.push({
      angle: i * angleStep,
      length: paddleBaseLength,
      width: paddleWidth,
      active: true,
    });
  }
}
</code></pre>
<p>This function creates paddles evenly distributed around the circle. Each paddle has:</p>
<ul>
<li>An angle (in radians) representing its position</li>
<li>A length (arc length along the circle)</li>
<li>A width (thickness)</li>
</ul>
<p><strong>Rotating Paddles</strong></p>
<pre><code class="language-javascript">function updatePaddles() {
  if (rightPressed) {
    paddles.forEach((paddle) => {
      paddle.angle += paddleRotationSpeed;
    });
  } else if (leftPressed) {
    paddles.forEach((paddle) => {
      paddle.angle -= paddleRotationSpeed;
    });
  }
}
</code></pre>
<p>When the player presses the left or right arrow keys, all paddles rotate in the corresponding direction at a constant speed.</p>
<p><strong>Drawing Paddles</strong></p>
<pre><code class="language-javascript">function renderGame() {
  paddles.forEach((paddle) => {
    const paddleAngle = paddle.angle;
    const paddleLength = paddle.length;

    const halfLength = paddleLength / 2;
    const startAngle = paddleAngle - halfLength / arenaRadius;
    const endAngle = paddleAngle + halfLength / arenaRadius;

    ctx.beginPath();
    ctx.arc(centerX, centerY, arenaRadius, startAngle, endAngle);
    ctx.lineWidth = paddle.width;
    ctx.strokeStyle = "#FFF";

    ctx.stroke();
  });
}
</code></pre>
<p>The challenge here is that the paddles are drawn as arc segments on the arena's perimeter rather than the tradional rectangles. To do this:</p>
<ol>
<li>We calculate the start and end angles for each paddle</li>
<li>The formula <code>paddleAngle ± (halfLength / arenaRadius)</code> converts the paddle's linear length to an angular span</li>
<li>The <code>ctx.arc()</code> method draws the arc segment at the arena's radius</li>
<li>We set <code>lineWidth</code> to control the paddle's thickness</li>
<li>Special effects like the flashing colors I added later for power ups are applied conditionally</li>
</ol>
<p><strong>Patterned Paddles</strong></p>
<p>There was another challenge with the paddles. In the original game, they were represented with yellow and black diagonal lines as traditionally seen in construction signs. I really wanted to implement this by my canvas was drawing an arc.</p>
<pre><code class="language-javascript">function createHazardPattern() {
  const patternCanvas = document.createElement("canvas");
  const patternContext = patternCanvas.getContext("2d");

  patternCanvas.width = 20;
  patternCanvas.height = 20;

  patternContext.fillStyle = "#FFD700";
  patternContext.fillRect(0, 0, 20, 20);

  patternContext.fillStyle = "#000000";
  patternContext.beginPath();
  for (let i = -20; i &#x3C; 40; i += 20) {
    patternContext.moveTo(i, 0);
    patternContext.lineTo(i + 10, 0);
    patternContext.lineTo(i + 30, 20);
    patternContext.lineTo(i + 20, 20);
    patternContext.closePath();
  }
  patternContext.fill();

  return ctx.createPattern(patternCanvas, "repeat");
}
</code></pre>
<p>This code draws a hazard pattern which can be applied to the paddles. However, we couldn't apply it directly as the pattern needed to rotate as the paddles rotated! So instead, we clip the paddle shape and apply a pattern to a rectangle drawn around the paddle.</p>
<pre><code class="language-javascript">paddles.forEach((paddle) => {
  // as above
  const paddleAngle = paddle.angle;
  const paddleLength = paddle.length;

  const halfLength = paddleLength / 2;
  const startAngle = paddleAngle - halfLength / arenaRadius;
  const endAngle = paddleAngle + halfLength / arenaRadius;

  ctx.save();

  const outerRadius = arenaRadius + paddle.width / 2;
  const innerRadius = arenaRadius - paddle.width / 2;

  ctx.beginPath();
  ctx.arc(centerX, centerY, outerRadius, startAngle, endAngle);
  ctx.lineTo(
    centerX + innerRadius * Math.cos(endAngle),
    centerY + innerRadius * Math.sin(endAngle)
  );
  ctx.arc(centerX, centerY, innerRadius, endAngle, startAngle, true);
  ctx.closePath();

  ctx.clip();

  ctx.translate(centerX, centerY);
  ctx.rotate(paddleAngle);
  ctx.fillStyle = hazardPattern;
  ctx.fillRect(
    -arenaRadius * 1.5,
    -arenaRadius * 1.5,
    arenaRadius * 3,
    arenaRadius * 3
  );

  ctx.restore();
});
</code></pre>
<h3>Handling Collisions</h3>
<p>The collision system determines what happens when a ball hits (or misses) a paddle.</p>
<p><strong>Ball Movement</strong></p>
<pre><code class="language-javascript">  ball.x += Math.cos(ball.angle) * ball.speed * speedMultiplier;
  ball.y += Math.sin(ball.angle) * ball.speed * speedMultiplier;

  const dx = ball.x - centerX;
  const dy = ball.y - centerY;
  const distance = Math.sqrt(dx * dx + dy * dy);

  if (distance + ball.radius > arenaRadius) {
    if (!checkPaddleCollision(ball)) {
      // Ball escaped, handle life loss...
    }
  }
}
</code></pre>
<p>For each ball:</p>
<ol>
<li>We update its position based on angle and speed</li>
<li>We calculate its distance from the center</li>
<li>If the ball reaches the arena boundary, we check for paddle collisions</li>
</ol>
<p>For the movement, the game uses polar coordinates converted to Cartesian (x,y) movement. The <code>ball.angle</code> represents the direction of travel in radians (0 to 2π) with <code>Math.cos(ball.angle)</code> calculating the x-component of movement and <code>Math.sin(ball.angle)</code> calculates the y-component.</p>
<p>To help visualise the movement, consider:</p>
<ul>
<li><strong>When ball.angle = 0:</strong> Ball moves right (cos(0)=1, sin(0)=0)</li>
<li><strong>When ball.angle = π/2 (90°):</strong> Ball moves down (cos(π/2)=0, sin(π/2)=1)</li>
<li><strong>When ball.angle = π (180°):</strong> Ball moves left (cos(π)=-1, sin(π)=0)</li>
<li><strong>When ball.angle = 3π/2 (270°):</strong> Ball moves up (cos(3π/2)=0, sin(3π/2)=-1)</li>
</ul>
<p><strong>Paddle Collision Detection</strong></p>
<pre><code class="language-javascript">function checkPaddleCollision(ball) {
  const dx = ball.x - centerX;
  const dy = ball.y - centerY;
  const ballAngle = Math.atan2(dy, dx);

  for (const paddle of paddles) {
    let angleDiff = (ballAngle - paddle.angle) % (2 * Math.PI);
    if (angleDiff &#x3C; 0) angleDiff += 2 * Math.PI;
    if (angleDiff > Math.PI) angleDiff = 2 * Math.PI - angleDiff;

    const arcLength = angleDiff * arenaRadius;

    if (arcLength &#x3C;= paddle.length / 2) {
      return true;
    }
  }

  return false;
}
</code></pre>
<p>The collision detection has several key steps:</p>
<ol>
<li>Calculate the angle between the center and the ball's position</li>
<li>For each paddle, find the angular difference between the ball and paddle</li>
<li>Convert this angular difference to an arc length along the arena boundary</li>
<li>If this arc length is less than half the paddle's length, a collision occurred</li>
</ol>
<h3>Power-up System</h3>
<p>The original Containment game didn't include power-ups. When I played around with the original, it felt like it would benefit from such a feature. Originally power-ups spawned from the middle circle in the same way the ball did. But it wasn't satisifying and it felt too difficult to obtain a power up. So instead, I moved the power ups to appear in an empty space around the arena boundary, and so can be collected by paddles as they rotate. Power ups include gaining an extra life or getting a bigger paddle, but can also include negative effects like a faster ball or smaller paddle. As there are dual paddles, it becomes a challenge for the user to avoid certain power-ups. However, successfully using these negative power ups provides a larger score increase.</p>
<p><strong>Spawning Power-ups</strong></p>
<p>Power-ups spawn with these characteristics:</p>
<ol>
<li>A random type is selected</li>
<li>The system tries to position them away from paddles (to prevent immediate collection)</li>
<li>They appear exactly on the arena boundary</li>
<li>They have visual effects (fade in/out) and a limited lifetime</li>
</ol>
<pre><code class="language-javascript">function spawnPowerUp() {
  const randomIndex = Math.floor(Math.random() * powerUpTypes.length);
  const powerUpType = powerUpTypes[randomIndex];

  let angle;
  let validPosition = false;
  let maxAttempts = 10;
  let attempts = 0;

  const posX = centerX + Math.cos(angle) * arenaRadius;
  const posY = centerY + Math.sin(angle) * arenaRadius;

  // Create the power-up
  powerUps.push({
    x: posX,
    y: posY,
    type: powerUpType,
    expiresAt: Date.now() + 2000,
    opacity: 0,
    fadeDirection: "in",
    fadeSpeed: 0.05,
  });
}
</code></pre>
<h3>Power-up Collision Detection</h3>
<p>For collision detection, we calculate the angular distance between the power-up and the paddle. There's a lot of Math involved here, and Claude AI was great in doing the heavy lifting.</p>
<pre><code class="language-javascript">for (const paddle of paddles) {
  let angleDiff = Math.abs(angle - paddle.angle) % (Math.PI * 2);
  if (angleDiff > Math.PI) angleDiff = Math.PI * 2 - angleDiff;

  const arcLength = angleDiff * arenaRadius;

  if (arcLength &#x3C;= paddle.length / 2 + powerUpRadius) {
    activatePowerUp(powerUp.type);

    showPowerUpMessage(powerUp.type);

    powerUps.splice(i, 1);
    break;
  }
}
</code></pre>
<p>The collision detection is similar to ball collisions, but with a more generous collision area.</p>
<p>Each power-up:</p>
<ol>
<li>Applies an immediate effect (changing paddle size, adding balls, etc.)</li>
<li>May have visual feedback (flashing)</li>
<li>For timed effects, schedules a deactivation</li>
</ol>
<h3>Particles</h3>
<p>Although I've written a fair number of particle systems in my time, I got AI to help me write all of the particle systems for this project. It wrote it far quicker than I would and created more impressive effects.</p>
<p>After playing around with space starfield background for a while, I asked AI to produce a Bokeh effect for the background instead which looked really cool. When implementing changing the colours of the Bokeh effect when the score changed, I accidentally stumbled across a way to make the background interact with the game. I had to tone down the intensity as it started to make me feel sick, but I think the tamer version helps tie everything together.</p>
<h3>Music and Sound Effects</h3>
<p>For the music, I wanted to create something quite basey. I took inspiration from Disclosure - King Steps. I asked Claude AI to summarise the beat and sounds of the song.</p>
<pre><code>Please describe the beat and sounds of King Steps by Disclosure. Ignore the vocals, imagine you had to ask someone to replicate the music. Describe the sound in 200 characters.
</code></pre>
<p>It output the following:</p>
<blockquote>
<p>Four-on-the-floor house beat with crisp hi-hats &#x26; punchy kicks. Deep, wobbling bass groove creates momentum. Atmospheric synth pads float overhead while melodic synth hooks provide catchy motifs.</p>
</blockquote>
<p>I then took that prompt in to <a href="https://aimusic.so/">AI Music.so</a> and generated two tracks which are used in the game (chosen at random).</p>
<p>Sound effects came from <a href="https://mixkit.co/free-sound-effects">Mixkit.co</a> which is an awesome resource that doesn't require a sign in for a lot of sounds.</p>
<p>I decided the power ups needed to be more prominently announced, so I used <a href="https://elevenlabs.io/app/speech-synthesis/text-to-speech">ElevenLabs text-to-speech</a> to produce spoken words for each. The voice is Brian on a very slow speed.</p>
<h3>Conclusion</h3>
<p>In the days gone past, building a indie game like Containment would either mean skipping extra visual or sound effects, or spending a unreasonably large amount of time on getting it right.</p>
<p>AI has helped me recreate this game which had been lost to old versions of Windows, and I'm thankful for that. However, if you start to dig around the source code you'll see it has become quite a mess. I decided to stay relatively hands-off in this project, only intervening when it was easier to make a manual edit (e.g. update a power up icon) or one bizarre time where Cursor told me I needed to go and manually remove a bracket as it couldn't do it.</p>
<p>So there's still a way to go with AI, but given this project wouldn't exist at all without AI I can live with some messy code.</p>
<p><a href="/games/containment/index.html">Play Containment DX here</a>!</p>]]></content:encoded>
      <pubDate>Mon, 03 Mar 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Mastering NextJS Parallel Routes</title>
      <link>https://www.andismith.com/blogs/2025/02/mastering-nextjs-parallel-routes</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/02/mastering-nextjs-parallel-routes</guid>
      <description>Recently I came across a powerful routing feature in Next.js that completely changed how I thought about structured complex web applications.</description>
      <content:encoded><![CDATA[<p><strong>Recently I came across a powerful routing feature in Next.js that completely changed how I thought about structured complex web applications.</strong></p>
<p>I had an application where there were multiple components which needed to load and change independently. My use case was more than just needing different props - I essentially needed to have multiple children that sat in different parts of my layout.</p>
<p>And that's when I discovered NextJS Parallel Routes, which were introduced in NextJS 14.</p>
<h2>What Are Parallel Routes?</h2>
<p>Parallel Routes are an advanced routing mechanism that lets you show multiple pages at once within the same layout.</p>
<p>In regular websites, when you click a link, the whole page changes. But with Parallel Routes, only certain parts change while others stay the same. This is perfect for:</p>
<ul>
<li>Data-heavy dashboards where you need to see multiple sections</li>
<li>An e-commerce site where you want to show products and a trolley independently</li>
<li>Social media feeds with different content types</li>
<li>Authentication flows where only part of the page changes when the user is logged in</li>
</ul>
<h2>How Do Parallel Routes Work?</h2>
<p>The magic of Parallel Routes happens with special folders known as 'slots' which are prefixed with an <code>@</code> symbol. For example <code>@analytics</code>.
Let's say you're building a dashboard with three sections:</p>
<ol>
<li>Main dashboard</li>
<li>Analytics section</li>
<li>Revenue section</li>
</ol>
<p>Here's how you'd set up your folder structure:</p>
<pre><code>app/
└── dashboard/
    ├── layout.tsx
    ├── page.tsx (Main dashboard page)
    ├── @analytics/
    │   └── page.tsx (Analytics section)
    └── @revenue/
        └── page.tsx (Revenue section)
</code></pre>
<p>These slots automatically become props that are passed to the layout - you don't need to import them. So you can use them like so:</p>
<pre><code>export default function DashboardLayout({
  children,
  analytics,
  revenue,
}) {
  return (
    &#x3C;div>
      &#x3C;nav>Dashboard Links&#x3C;/nav>
      &#x3C;div>{children}&#x3C;/div>
      &#x3C;div>{analytics}&#x3C;/div>
      &#x3C;div>{revenue}&#x3C;/div>
    &#x3C;/div>
  );
}
</code></pre>
<p>Now you understand how they work, let's take a look at some of the cool things you can do with them:</p>
<h3>1. Independent Loading and Error States</h3>
<p>NextJS's App Router already has <a href="https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming">great handling for loading states</a>, and this is extended with parallel routes which are able to have their own loading states!</p>
<p>This enables you to partially load your page and allow your users to interact even while other sections are still loading. Like you would for any NextJS loading state you can add loading to parallel route by simply adding a <code>loading.tsx</code> file inside each folder. e.g:</p>
<pre><code>app/dashboard/@analytics/loading.tsx
app/dashboard/@revenue/loading.tsx
</code></pre>
<p>The same is the case for error handling. Just add an <code>error.tsx</code> file in each section's folder, and if something goes wrong, only that section will show an error while the rest of the page continues working.</p>
<h3>2. Conditional Rendering</h3>
<p>It's up to you what you do with the props sent to the <code>layout.tsx</code> file - you don't have to display them. Instead, you may want to show different sections based on conditions. For example, show an admin panel only if the user is an admin, or a log in screen if the user is not logged in:</p>
<pre><code class="language-jsx">export default function Layout({ admin, user }) {
  const isAdmin = checkIfUserIsAdmin();

  return isAdmin ? admin : user;
}
</code></pre>
<h3>3. Creating Modals</h3>
<p>You can also make pop-up windows that can be bookmarked and shared, and maintain their state when refreshed.</p>
<pre><code>app/
├── layout.tsx
├── page.tsx
└── @auth/
    ├── default.tsx (Shows nothing)
    └── (.)login/
        └── page.tsx (Shows login modal)
</code></pre>
<p>The modal becomes a real route in your application while maintaining the user experience of a traditional modal.</p>
<h2>Navigating with Parallel Routes</h2>
<p>Things start to get a little more complicated when you start navigating beyond your initial page in to child pages of your content. By default, NextJS will look for a similar child page in your slot.</p>
<p>So, for example, if you had a page at <code>dashboard/settings</code>, NextJS would look for a <code>@analytics/settings</code> too instead of using the original component.</p>
<p>There are a couple of ways to resolve this - the first is by creating a <code>default.tsx</code> page to show a fallback. However, this starts to cause complications with your state when you are changing between server (when a user initially lands on your page) and client (when a user navigates to your page) side rendering.</p>
<p>A safer bet is to create a catch all folder using the same mechanic you'd use to create a NextJS slug - for example, <code>[...catch]/page.tsx</code>. This will catch any pages that do not exist and serve this page instead.</p>
<h2>Why Parallel Routes Are Awesome</h2>
<ul>
<li>They make complex layouts simpler</li>
<li>Each section can have its own loading state and error handling</li>
<li>You can stream content as it becomes ready</li>
<li>Perfect for conditional content (like showing different things to logged-in users)</li>
<li>They work great with tabs, modals, and multi-section pages</li>
<li>Makes your code more organized by keeping related features together</li>
</ul>
<p>Next time you need to build a dashboard or any page with multiple independent sections, give Parallel Routes a try! It's your new superpower for website routing!</p>]]></content:encoded>
      <pubDate>Mon, 24 Feb 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>The Speed Paradox</title>
      <link>https://www.andismith.com/blogs/2025/02/the-speed-paradox</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/02/the-speed-paradox</guid>
      <description>Why moving faster often means slowing down first.</description>
      <content:encoded><![CDATA[<p><strong>Why moving faster often means slowing down first</strong></p>
<p>If you've ever had to deal with stakeholder management as an engineering leader, there's undoubtedly one question that keeps resurfacing again and again: "How do we make the developers go faster?"</p>
<p>The pressures of a deadline and the risk of being too late to the market - particularly in startups - and a common misconception about software development productivity often drive this feeling of not going fast enough - but what can we do about it?</p>
<h2>The Velocity Trap</h2>
<p>Early in my career as a technical leader, I fell into a velocity trap – we just needed to write more code in less time. The problem is, people who chase this goal quickly find themselves running faster toward bigger problems.</p>
<p>The reality I've learned is counterintuitive: sustainable speed requires intentional slowdown at critical moments.</p>
<h2>The Hidden Costs of Cutting Corners</h2>
<p>We've all been there - when the time spent firefighting overtakes the time spent building new value. It can normally be traced back to one of the following - skipping proper requirement gathering; failing to get team agreement; rushing through architecture and design thinking or just coding without thinking.</p>
<p>Each shortcut taken compounds, creating a snowball effect that eventually brings development to a crawl and the result is we spend the next three months firefighting production issues, dealing with unhappy customers, and ultimately rewriting large portions of the codebase.</p>
<p>Don't get me wrong - sometimes it's ok to cut corners. If you have a short lived project or minisite, then you build it in a different way than you would an entire digital platform. As a leader we need to recognise when it's ok to apply speed vs move slower.</p>
<h2>So How Can We Go Faster?</h2>
<p>As leaders, there's still things we can do we speed up development without taking these shortcuts:</p>
<ul>
<li>
<p><strong>Should we even do this?</strong> - To quote the book Rework, from the team at 37signals, it is "better to build half a product, than a half assed product". Make a small but great thing, rather than a big but bad thing. And sometimes, maybe you don't need to build the feature at all. Your team will thank you for not wasting their time.</p>
</li>
<li>
<p><strong>Focus on execution</strong> - Execution isn't about doing more things, but about doing the right things well. To do this, you need to:</p>
<ul>
<li>Identify the core value drivers that matter most to users</li>
<li>Resist the temptation to spread resources too thin</li>
<li>Measure impact, not just output</li>
<li>Create space for deep work and meaningful iteration</li>
</ul>
</li>
<li>
<p><strong>Starting in manual mode</strong> - Rather than automating everything at the start, look at what you can do manually. I've used this technique a lot to help gauge what we should and shouldn't do; and when you know it works and it's starting to become a scaling problem, then you look to make it automated. If it doesn't work - great don't build it.</p>
</li>
<li>
<p><strong>Buy vs build</strong> - At my last startup, we needed to deliver a lot of features in a short amount of time - so we looked at what was truely important to the product - the differential - and we found off-the-shelf solutions for the remaining items.</p>
</li>
<li>
<p><strong>Be realistic with what you can achieve</strong> - Heroics only get you so far. Estimating story points with only the best case scenario never works - there will be issues with the approach and your developer will get distracted by other meetings, tasks or other people. Even if they manage to get a day or two of complete uninterruption, it won't last. There will be unplanned work - like finding a bug in production or a unhappy client. So listen to your team's capacity signals and make sure you account for them before agreeing on a release date with the business.</p>
</li>
<li>
<p><strong>Understand how your team works</strong> - As a leader, it is your responsibility to learn how your team performs best and give them that environment. I often find a happy team is a more productive team - and you can achieve a happy team by fostering an environment where they can succeed without fear of failure, where no question is a stupid question and without constant distraction.</p>
</li>
<li>
<p><strong>Avoid burnout</strong> - You need to know how far you can push your team and when you need to stop pushing. If your a good leader, your team will go that extra mile for you from time to time. But if you keep asking to go the extra mile, then they will burnout and start to look elsewhere.</p>
</li>
</ul>
<p>Essentially, what we are looking for is sustainable, consistent progress.</p>
<h2>What Else Can We Do?</h2>
<p>Of course, there are other things we can do as leaders to help our teams:</p>
<ul>
<li>
<p><strong>Reduce the distractions</strong> - How important is the production bug? Is it affecting a few or a lot of users? Similarly, do they really need to be in every meeting - or could this meeting be shared as a Loom instead? A lot of companies look at meeting-free days as a way to allow for improved productivity. Agreeing with your team if there's a day or time of day (e.g any meetings should happen in the mornings) can help.</p>
</li>
<li>
<p><strong>Keep the focus on now</strong> - Allow the team to focus on the current priorities rather than what is coming along the pipeline in two or three months time.</p>
</li>
<li>
<p><strong>Tell your team it's ok to be in focus time</strong> - Set expectations around when you want them to be active on Slack, Teams, email etc. Sometimes we feel constant pressure to reply on Slack to prove we are working. I normally tell my team to answer when they have time, and follow more of a path toward asynchronous team communication.</p>
</li>
<li>
<p><strong>Make sure you have some time for being social</strong> - Allow time for your team to have fun and let off steam. There are lots of online games you can play with remote teams, or a coffee or bar visit for those who are in office. Sprint demos are also a great way to celebrate the work achieved and spend some time understanding what everyone has sacrificed to reach that point.</p>
</li>
</ul>]]></content:encoded>
      <pubDate>Thu, 13 Feb 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Solving InfoSec with AI</title>
      <link>https://www.andismith.com/blogs/2025/01/solving-infosec-with-ai</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2025/01/solving-infosec-with-ai</guid>
      <description>Filling in InfoSec surveys is rarely fun at the best of times. How can we use AI to help?</description>
      <content:encoded><![CDATA[<p><strong>Filling in InfoSec surveys is rarely fun at the best of times. So how can we use AI to help?</strong></p>
<p>If you're working in a B2B business in some kind of security capacity, chances are that you spent a fair amount of time filling in InfoSec surveys from perspective clients. These surveys are important to make clients feel at ease, but they can be painstaking to fill in; they often come in different formats and with slightly different wording and they can take a serious chunk of time to complete.</p>
<h3>Enter Claude</h3>
<p>I've been using Claude 3.5 Sonnet and it's <a href="https://claude.ai/projects">Projects</a> feature to take the pain out of answering these questions. It's not perfect - but it gives a good starting point which you can then review and submit.</p>
<p>To do this you will need:</p>
<ul>
<li>A paid Claude account.</li>
<li>All of the InfoSec policies and agreements your company has.</li>
<li>Your company's privacy policy.</li>
<li>Any previous InfoSec surveys that are relevant.</li>
</ul>
<h3>How to make your InfoSec life better</h3>
<p><strong>1. Create a new project in Claude AI</strong></p>
<p>Go to <a href="https://claude.ai/projects">Claude Projects</a> and select 'Create Project'. Give your project a name - e.g. 'InfoSec' and a description - e.g. Answer questions on InfoSec.</p>
<p>Once it is created, check your project is private by checking the padlock is showing next to the project name.</p>
<p><strong>2. Add the project instructions</strong></p>
<p>On the new project page, click 'Set project instructions' and add the following prompt:</p>
<blockquote>
<p>You are an InfoSec expert who works for <code>[Company name]</code>. <code>[Add description of company]</code>.</p>
<p>You will be given prospective client security questions and you will need to provide accurate client-facing answers.</p>
<ul>
<li>Use a professional tone, use precise wording.</li>
<li>The response is for a technical audience, so you can use common technology and security terms, but keep the wording simple.</li>
<li>You should not say "Based on the information available", you know the information.</li>
<li>Keep answers optimistic</li>
<li>Always start with a positive outlook - your answers may determine whether the prospective client becomes a paying customer.</li>
<li>Answers should be concise and kept to less than 250 words.</li>
<li>Avoid becoming a sales person and promoting the product in your answers. This is for InfoSec.</li>
</ul>
</blockquote>
<p><strong>3. Upload your data</strong></p>
<p>Now it's time to add your project data. Upload your policies to the project. Some of the policies you may want to add include:</p>
<ul>
<li>Acceptable Use</li>
<li>Asset Management Policy</li>
<li>Backup Policy</li>
<li>Change Management Policy</li>
<li>Code of Conduct</li>
<li>Data Classification Policy</li>
<li>Data Protection Policy</li>
<li>Data Retention Policy</li>
<li>Encryption Policy</li>
<li>Information Security Policy</li>
<li>Logging and Monitoring Policy</li>
<li>Password Policy</li>
<li>Physical Security Policy</li>
<li>Risk Assessment Policy</li>
<li>System Access Control Policy</li>
<li>Vendor Management Policy</li>
<li>Vulnerability Management Policy</li>
</ul>
<p>You may also want to include:</p>
<ul>
<li>Business Continuity Plan</li>
<li>Disaster Recovery Plan</li>
<li>Incident Response Plan</li>
<li>A description of the system tech stack</li>
<li>Software Development LifeCycle</li>
</ul>
<p>If you have any Data Processing Agreements or Master Service Agreements you can also include these.</p>
<p><strong>4. Adjust the writing style</strong></p>
<p>Once you have uploaded some documents, Claude allows you to configure a custom writing style. Under the text entry area and next to the model name is a 'Choose style' option. If you click 'Create', you can add some of your previously answered questions and AI will respond with a similar style.</p>
<p><strong>5. Test your output</strong></p>
<p>All that is left to do now is test your output! Give it a whirl and then see what needs to be tweaked!</p>
<p>And that's how you make InfoSec documentation more innovative!</p>]]></content:encoded>
      <pubDate>Tue, 28 Jan 2025 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Let&apos;s Talk About 1:1s</title>
      <link>https://www.andismith.com/blogs/2024/11/lets-talk-about-1-1s</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2024/11/lets-talk-about-1-1s</guid>
      <description>Should you be running 1:1s with your team?</description>
      <content:encoded><![CDATA[<p><strong>Should you be running 1:1s with your team?</strong></p>
<p>There's been a lot of noise in the last couple of weeks over <a href="https://x.com/asanwal/status/1856549582332727471">an interview with Brian Chesky</a> from Airbnb on 1-1s (one to ones).</p>
<blockquote>
<p>I don't believe in 1-on-1s and almost no great CEO in history has ever done 1-on-1s</p>
</blockquote>
<p>It's an interesting statement - for as long as I can remember we've always been told how important it is to do 1:1s with your team. Should we stop?</p>
<h2>Communication is key</h2>
<p>Regardless of your stance on 1-1s, you have to agree that communication is a key to success in any business. If you aren't having 1-1s then having those discussions with the team in a different format is still important for visibility you still need to know what is going on in the business. The problem with having these discussions in a different format is you no longer have a safe space for your team to talk to you about issues.</p>
<p>Sometimes an issue can't be properly discussed in an office or group setting, and by cancelling or removing their 1-1s what you are actually saying to your direct reports is that you don't care about them or their career. They are just an expendable resource to you.</p>
<p>Ouch, that doesn't feel good.</p>
<h2>Building relationships</h2>
<p>As a leader, I find 1-1 catch ups are important to ensure a good healthy relationship with your reports and ultimately to boost productivity, morale and engagement. It allows you to build a rapport with your team in a non-stressful environment. It allows you to understand about how your team think and communicate. And this becomes an incredibly powerful tool for situations where things do get stressful.</p>
<h2>What makes a successful 1-1?</h2>
<p>For 1-1s to be successful, they should be:</p>
<ul>
<li><strong>Regular</strong> - An meeting that both of you expect to happen on the same day at the same time each week (or 2 weeks).</li>
<li><strong>Face-to-face</strong> - either in-person or via a video call.</li>
<li><strong>Private</strong> - a safe space to discuss and feel comfortable about discussing anything.</li>
<li><strong>Personal</strong> - It should be about your direct report and their needs, not about the status of a project.</li>
</ul>
<p>So as leaders, how should we be starting a 1-1? I've recently been reading a book called <a href="https://www.mbs.works/coaching-habit-book/">"The Coaching Habit" by Michael Bungay Stanier</a> which covers exactly how to ask a kickstart question, and it's actually very simple.</p>
<blockquote>
<p>What's on your mind?</p>
</blockquote>
<p>It's a question that gets right to the heart of the problem and allows our reports to immediately feel safe enough to talk about what they've been thinking about.</p>
<p>Now the question is out there, it's time for us as leaders to be good listeners, be genuinely interested in what your report has to say and actually listen to the answer.</p>
<p>Through this answer you want to understand if they are feeling supported; what is concerning them; what are the opportunities and if they have any news or feedback. If you don't feel like the first question has given you these answers, then there is a very simple follow-up question.</p>
<blockquote>
<p>And what else?</p>
</blockquote>
<h2>1-1s are an integral part of working in a team</h2>
<p>1-1s are important for the line report to have a space to feel heard and important, and they are an important tool for a manager to understand how your report is doing. It is the only meeting where you have the undivided attention of the other person.</p>
<p>They are also a chance for you to give feedback on ways of working and share what is on your mind.</p>
<p>If you want to get better at 1-1s, I strongly recommend The Coaching Habit for opening your eyes on how you can better communicate with your team and have more useful and productive 1-1s. And remember, communication is key.</p>]]></content:encoded>
      <pubDate>Sat, 23 Nov 2024 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Do You Really Need Your Own AI Model?</title>
      <link>https://www.andismith.com/blogs/2024/08/do-you-really-need-your-own-ai-model</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2024/08/do-you-really-need-your-own-ai-model</guid>
      <description>With advancements in AI moving so fast, is it worth the return on investment?</description>
      <content:encoded><![CDATA[<p><strong>With advancements in AI moving so fast, is it worth the return on investment?</strong></p>
<p>We've seen a real change in AI over the last year or so. OpenAI's <a href="https://openai.com/index/gpt-4/">release of GPT-4</a> really changed the AI landscape; Claude's Sonnet 3.5 which <a href="https://www.anthropic.com/news/claude-3-5-sonnet">released in June</a> is equally impressive; and Google's Gemini is also not too far behind.</p>
<p>The speed at which these companies are moving forward and upgrading their AI offerings is staggering. New features and new releases are available frequently which substantial new features such as OpenAI's Assistant Threads and Claude's Projects and Artifacts.</p>
<p>Whilst building your own AI model seemed like a viable option just two years ago, now it feels like something that is difficult to recommend. Any development is going to become quickly out of date without serious investment. And the returns on that investment are going to be difficult to achieve unless your moat is wide and use case is incredibly unique.</p>
<h2>Supplementing knowledge</h2>
<p>Instead, using techniques such as <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">Retrieval-Augmented Generation</a> (RAG) seem to be more efficient approach. RAG supplements the AI model with additional information through either structured documents or a vector database to allow the AI to be better informed about the context in which the user is interacting with AI. For example, if your AI was providing career advice to the user you may use RAG to supply the users' work history, some information about the companies they have worked for and information about the job role they are applying for.</p>
<p>With these techniques, you can get the best of both worlds - all the updates and model improvements from the heavy hitters with the context from your own business - and at a fraction of the cost.</p>
<h2>When should we use a model?</h2>
<p>There are of course times when using a model is still worth it. You should consider using a model when:</p>
<ul>
<li>You have highly specialised domain data that existing models do not understand</li>
<li>You need complete control over the model's behaviour</li>
<li>You have strict privacy requirements</li>
</ul>
<h2>Removing the stigma of wrappers</h2>
<p>Over the last 12 months there's been a bit of a stigma put on AI wrappers. A feeling that if you are not using your own model then what is the moat of your business. But I feel this stigma is unfair.</p>
<p>If we think about a lot of successful products, quite often what we find is that users are looking for convienence. If you can cut the time down that it takes a user to complete a task (and you market it well), then they really don't care about how you are doing it.</p>
<p>At this point in time, combining AI wrappers with Retrieval-Augmented Generation feels like a very valid way to build a successful business; doesn't require deep machine learning expertise and has a much shorter time to market. The challenge from an entrepreneurial point of view is to find a pain point that people would be willing to pay money toward fixing and finding a way to distribute and market your application.</p>
<p>And that, dear reader, is up to you.</p>]]></content:encoded>
      <pubDate>Sun, 04 Aug 2024 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Chaining AI Assistants with Open AI Threads</title>
      <link>https://www.andismith.com/blogs/2024/06/chaining-ai-assistants-with-open-ai-threads</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2024/06/chaining-ai-assistants-with-open-ai-threads</guid>
      <description>How to make AI even smarter.</description>
      <content:encoded><![CDATA[<p><strong>How to make AI even smarter.</strong></p>
<p>AI agents aren't a new concept. LangChain has been around for quite some time - but I hear a lot of people have pain with it in production environments and the AI landscape is moving on so quickly with OpenAI can I find a better way to create chained AI agents?</p>
<p>Turns out I can. What follows is an application that can work across multiple contexts whilst maintaining a consistent chat history.</p>
<h2>The concept</h2>
<p>The concept of this approach is as follows:</p>
<p>Whenever a message is sent to the application, a proxy router agent analyses the message and determines which 'journey' the user should be on. Depending on the journey, the router will pass the message to one of many AI agents who each specialise in completing a different job.</p>
<p>The router keeps a context of the user message stream so it can understand if the user is in an existing conversation with an agent, or it needs to pick an agent to start this conversation.</p>
<p>So the proxy router' response looks like this:
<code>{ agent: 'ONBOARD' }</code></p>
<p>While all the other AI agents respond with JSON in a format that looks more like this:
<code>{ content: 'This is the message', completed: true | false }</code></p>
<p>When an AI agent deems a conversation as completed, it will set the <code>completed</code> flag to true and the proxy router will then know to stop directing messages to that agent.</p>
<p>Like my previous article on <a href="/blogs/2023/09/ai-assistants-with-whatsapp">AI Assistants with WhatsApp</a>, for the demo data will be stored against an object locally. Obviously, in production you'd want an encrypted database.</p>
<h2>The proxy</h2>
<p>The proxy agent evaluates each request made to the system and works out the agent that should be used. The prompt would look something like the below, with the keywords matching the problem your AI assistant is trying to solve:</p>
<pre><code>You are an operator managing conversations with AI agents. You will be given one or more sentences that form part of a dialogue of a conversation and you need to respond with the relevant keyword.

The keywords are:
* "GREETING" - if the user says hello or hi
* "CONTINUE" - continue talking about the same subject. This should be used if the latest sentence is the same topic as the previous sentence.
* "HISTORY" - the user is referring to something they have spoke about before
* "PROGRESS" - the user wants to discuss their progress, understand what they are learning or change their focus
* "PROFILE" - the user wants to change or view their profile

You should respond with the JSON:

{ "agent": "GREETING" | "CONTINUE" | "HISTORY" | "PROGRESS" | "PROFILE" }
</code></pre>
<p>The proxy service uses this prompt to decide where to send the user request. We simply pass in the user request in to</p>
<pre><code>import openai from "../lib/openai";

const prompt = 'OUR PROMPT';

type Message = {
  content: string;
  role: "system" | "user" | "assistant";
  type?: string;
};

async function main(request: string) {
  if (!request) {
    return { agent: "ERROR", message: "Invalid request" };
  }

  if (request.toUpperCase() === "HELP") {
    return { agent: "HELP" };
  }
</code></pre>
<p>We have a shortcut method of 'HELP' for if a user gets stuck in a path.</p>
<pre><code>  try {
    const chatCompletion = await openai.chat.completions.create({
      messages: [
        { role: "system", content: prompt },
        { role: "user", content: request },
      ],
      model: "gpt-4o", // you could use a different model here
      response_format: { type: "json_object" },
    });

    const response = chatCompletion.choices[0].message.content;

    if (response) {
      try {
        return JSON.parse(response);
      } catch (error) {
        return { agent: "ERROR", message: error.message };
      }
    }

    return { agent: "ERROR", message: "Invalid response" };
  } catch (error) {
    return {
      agent: "ERROR",
      message:
        "Unable to contact the server at this time. Please try again later.",
    };
  }
}
</code></pre>
<p>The try/catch around the response parsing is to ensure we are receiving JSON from the AI response. Normally setting the type as json_object is enough, but it's worth having a safety check.</p>
<pre><code>async function routeChat(content: string, id: string): Promise&#x3C;RouteResponses> {
  const userData = getUserData(id);

  let route: Route = { agent: Agent.ONBOARDING };

  if (userData.state?.isOnboarded || false) {
    if (userData.state?.journey) {
      route = { agent: userData.state?.journey };
    } else {
      route = await proxy(content);
    }
  }

  let threadId = userData.threadId;

  if (!threadId) {
    const thread = await openai.beta.threads.create();
    threadId = thread.id;
    setThreadId(id, threadId);
  }

  let responseList: Array&#x3C;any> = [];

  if (route.agent === Agent.ONBOARDING) {
    const onboardingResponse = parseResponse(
      await onboarding(content, threadId, id)
    );
    responseList.push({
      ...onboardingResponse,
    });

    if (onboardingResponse.completed === true) {
      setUserData(id, { state: { isOnboarded: true, journey: Agent.TRIAGE } });
      const coachingResponse = parseResponse(await triage(content, threadId));
      responseList.push({
        ...coachingResponse,
        agent: Agent.TRIAGE,
      });
    }
  }

  if (route.agent === Agent.TRIAGE) {
    responseList.push(parseResponse(await triage(content, threadId)));
  }

  if (route.agent === Agent.COACH) {
    if (userData.state?.journey !== Agent.COACH) {
      setUserData(id, { state: { ...userData.state, journey: Agent.COACH } });
    }
    responseList.push(parseResponse(await coach(content, threadId)));
  }

  if (route.agent === Agent.GREETING) {
    responseList.push(greeting(userData));
  }

  if (route.agent === "HELP") {
    responseList.push({
      content: `I can help you with X.

      What would you like to talk about?`,
    });
  }
}
</code></pre>
<p>As you can see, each 'agent' uses the same consistent OpenAI thread, but has a different context.</p>]]></content:encoded>
      <pubDate>Fri, 28 Jun 2024 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Creating a Technical Strategy</title>
      <link>https://www.andismith.com/blogs/2024/02/creating-a-technical-strategy</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2024/02/creating-a-technical-strategy</guid>
      <description>A technical strategy is an important part of any technical leader&apos;s arsenal.</description>
      <content:encoded><![CDATA[<p><strong>A technical strategy is an important part of any technical leader's arsenal.</strong></p>
<p>Writing one involves understanding where the organisation currently is and what specific goals and objectives need to be achieved to align with the overall business goals. It can be used to provide transparency to the rest of the organisation on what your team are trying to achieve, and can be used as part of a business case for where you need investments to directly support broader business objectives.</p>
<p>I tend to split my technical strategies in to three sections:</p>
<ul>
<li>Where we are</li>
<li>Where we are going</li>
<li>How we are going to get there</li>
</ul>
<p>Let's take a closer look at those sections below.</p>
<h2>Where we are</h2>
<p>The "where we are" section looks at the current technological landscape for your organisation. It should give an overview of where you are at, and then discuss the strengths and weaknesses of your existing tech stack.</p>
<p>There may be many reasons why your tech stack needs improvement. Maybe your product requirements have changed and your stack isn't there right fit. Maybe technology has moved on and there is a better solution available. Maybe you took a lot of shortcuts to get to where you are now such as buy vs build. Maybe a competitor has found a better way.</p>
<h2>Where we are going</h2>
<p>The "where we are going" section should detail what the technical strategy is to meet the upcoming business goals.</p>
<p>Look at the vision and the mission of the company and that should guide how you prioritize what to work on. For example, if there are ambitious targets in the sales team then scaling the solution may be high on the list of what needs to be done. You can <a href="https://www.mindtools.com/a4wo118/smart-goals">use SMART goals</a> to help you understand what you need to accomplish.</p>
<h2>How we are going to get there</h2>
<p>The "how we are going to get there" is a great place to lay out your blueprint for the "what" needs to be done. It should be in a format that everyone can easily understand and refer to when they needed. As a technical leader, your team will use it to guide them in what to build whilst other teams and leaders will be able to use it to understand what you are planning to achieve.</p>
<p>With these three areas documented and updated as the business goals change, it will always be clear to the organisation how the technology teams are supporting the business.</p>]]></content:encoded>
      <pubDate>Sat, 17 Feb 2024 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Keeping a Daily Note</title>
      <link>https://www.andismith.com/blogs/2024/01/keeping-a-daily-note</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2024/01/keeping-a-daily-note</guid>
      <description>Starting the day right with a daily note.</description>
      <content:encoded><![CDATA[<p><strong>Starting the day right with a daily note.</strong></p>
<p>I'm a big fan of the <a href="https://obsidian.md/">Obsidian writing app</a> and have been using it's calendar and template plugins to produce a daily note template which I fill out each morning before stand-up.</p>
<p>I've experimented for a while with which details I would like to capture each day to plan and record my day, and here's where I have landed:</p>
<ol>
<li>I need to know how much of my day is going to be spent in meetings vs doing - so I track my meetings for the day. Ideally I would sync this to my calendar, but I haven't done this yet.</li>
<li>I need to prioritise my tasks. I'm using the <a href="https://www.developgoodhabits.com/rock-pebbles-sand/">time management analogy "rocks, pebbles and sand"</a> to ensure the important tasks are surfaced to the top.</li>
<li>I have some honest questions in there - I don't answer them every day, but I do think about them. For example, "What is keeping me at night?". It may be work related or personal, but it's something I may want to solve within today's schedule.</li>
<li>Finally I have some start of day and end of day tasks such as checking email, Slack and reviewing the teams progress and any new bugs identified. You can find new bugs in JIRA with the JQL query <code>issuetype = Bug AND created >= -1d ORDER BY created DESC</code>.</li>
</ol>
<p>So without further ado, here is my Markdown template!</p>
<pre><code>## Start of Day

* [ ] Check email
* [ ] Check Slack
* [ ] Review JIRA task board
* [ ] Review new JIRA bugs

## Thoughts

**What's keeping me up at night?**


**What do I feel stuck on?**


**What am I avoiding?**


**What's making me uncomfortable?**


## Today's Meetings

**Meetings duration: x hrs**
* [ ] 9:45 - 10:00 - Engineering Stand Up

## Today will be successful if...

?

## Today's Tasks

**≤ 1 Big Task**
* [ ] Task 1

**≤ 2 Medium Tasks**
* [ ] Task 1
* [ ] Task 2

**≤ 3 Small Tasks**
* [ ] Task 1
* [ ] Task 2
* [ ] Task 3

## End of Day

* [ ] Add tags to daily note
* [ ] Check email
* [ ] Clean up inbox
* [ ] Check tomorrow's calendar
* [ ] Clean up desktop, downloads

## Notes

#dailynote #jan2024 #year2024
</code></pre>]]></content:encoded>
      <pubDate>Mon, 08 Jan 2024 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Recovering from Failure</title>
      <link>https://www.andismith.com/blogs/2023/10/recovering-from-failure</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2023/10/recovering-from-failure</guid>
      <description>In case of emergency, read note. A post on preparing for incidents.</description>
      <content:encoded><![CDATA[<p><strong>In case of emergency, read note.</strong></p>
<blockquote>
<p>Being able to recover quickly from failure is more important than having failures less often.</p>
<ul>
<li>John Allspaw</li>
</ul>
</blockquote>
<p>In technology, failure is inevitable. Things <em>will</em> go wrong. So rather than hoping they don't, it's better to plan that they do.</p>
<p>Here are some of the things I think work well for when you have an incident. As you'll notice, they mostly require upfront planning. If you've found yourself already in the incident, it's likely too late.</p>
<h2>Know what to do</h2>
<p>Ensure there is a procedure for what to do in an incident. It should cover the following areas:</p>
<ul>
<li>Identification</li>
<li>Containment</li>
<li>Resolution</li>
<li>Maintenance/Actions</li>
</ul>
<h2>Know how to contact the team</h2>
<p>Set up a call tree with the team's numbers and locations. There may be a on-call rota, so link to that too.</p>
<p>Also include the numbers for stakeholders who will need to know how to handle customer complaints, the press .etc</p>
<h2>Create an incident check-list</h2>
<p>Often, incidents follow the same patterns so having a check-list will allow you to quickly find or disregard common issues. Some of the items to include on the list would be:</p>
<ul>
<li>Has there been a new deployment at the same time as the incident?</li>
<li>Has anyone changed a configuration setting recently?</li>
<li>Are our system dashboards reporting healthy?</li>
<li>Are any of our vendors down?</li>
<li>Is there a spike in errors in your logging software?</li>
<li>Have we hit any API limits?</li>
</ul>
<p>You can add links to the checklist so whoever is running the incident can just click through to check each one.</p>
<h2>Agree your war room strategy</h2>
<p>When there is an incident, know how your team will collaborate.</p>
<ul>
<li>Is it a video call or a Slack Huddle? Is it an existing room, or a room created especially for the incident?</li>
<li>Who will communicate to the rest of the business what is going on? How often will they do that?</li>
</ul>
<p>Agree on it and document it so everybody knows.</p>
<h2>Run a Post-Mortem</h2>
<p>Running a post-mortem on an incident is a great way to stop it happening again. Even smaller incidents can bring light to issues they may cause bigger problems later on.</p>
<p>The post-mortem should be carried out with the team that were involved in the incident and any other security or developers that work in that area. Allow the team to talk through the problem and capture notes.</p>
<p>Most importantly, make sure you review the incident reports once a quarter to ensure the actions recorded actually get actioned!</p>
<p>Here's a Markdown template of my Incident Post-Mortem report:</p>
<pre><code>## Executive Summary

- 3 or 4 bullet points of what went wrong and how it was resolved.

## Stats

- Number of affected users:
- Time to acknowledge:
- Time to resolve:
- Total downtime:

## Timeline of Events
| Date/Time | Event |
| --------- | ----- |
|           |       |

## Client Impact

Details on any annoyed clients or critical client problems that were caused by the incident.

## Analysis
* Lead-up:
* Fault:
* Detection:
* Response:
* Recovery:
* Recurrence:

## Root Cause (5 whys)
### Why?

### Why?

### Why?

### Why?

### Why?

## Resolution
### Short term
* What was done in the heat of the moment (or shortly after) to resolve?

### Long term
* What needs to be done longer term to stop this from happening again?

## Lessons Learned
* 2 or 3 bullet points on what was learnt

## Action Items
* Links to JIRA tickets with resolution dates

</code></pre>
<p>Good luck with your next incident!</p>]]></content:encoded>
      <pubDate>Sun, 29 Oct 2023 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>AI Assistants with WhatsApp</title>
      <link>https://www.andismith.com/blogs/2023/09/ai-assistants-with-whatsapp</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2023/09/ai-assistants-with-whatsapp</guid>
      <description>How to get started with building a WhatsApp AI Assistant.</description>
      <content:encoded><![CDATA[<p><strong>How to get started with building a WhatsApp AI Assistant.</strong></p>
<p><em>Please note: Since writing this article, OpenAI have introduced threads which is a far better way of handling message streams.</em></p>
<p>By now, you cannot have avoided hearing about the hype train that is ChatGPT. AI is here, and it's likely to take over a lot of the tasks we used to do manually.</p>
<p>One of the things ChatGPT is good for is impersonating a persona and having a discussion. In fact, it's pretty easy to build a simple wrapper to do just that and chat to AI on your favourite platform. Let's take a look at how we can do this with ChatGPT on WhatsApp using Twilio.</p>
<h2>How does it work?</h2>
<p>First, let's take a look at what we need to build:</p>
<p>DIAGRAM</p>
<p>So essentially we will build some kind of middleware that sits between WhatsApp and ChatGPT to be able to provide the right context to the system. In a production system, you would likely put this into a database but for the purposes of today's example, we will keep it in memory.</p>
<p>To run the AI Assistant, you'll need to set up a Twilio trial account. They have a WhatsApp sandbox service you can use until you are ready to use a real phone number.</p>
<h2>Building the app</h2>
<p>The app will run on Node.js and Express; use dotenv for environment configurations; and use the OpenAI and Twilio APIs.</p>
<p><code>npm install dotenv express openai twilio</code></p>
<p>We will have a single POST endpoint. I'm not going to detail how to use Express here - there are plenty of tutorials for that - but the base of your code will look something like this:</p>
<pre><code>const express = require("express");

const app = express();
const port = 3000;

app.use(express.json());

app.post('/message', async (req, res) => {
  // code will go here
});

app.listen(port, () => {
  console.log(`Starting app on port ${port}`)
})
</code></pre>
<p>We'll need to grab some API keys and config to make this work:</p>
<ul>
<li>An OpenAI key</li>
<li>A Twilio Account ID</li>
<li>A Twilio Auth Token</li>
<li>A phone number on Twilio</li>
</ul>
<p>These should live in a .env file like the one below:</p>
<pre><code>TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_NUMBER=
OPENAI_API_KEY=
</code></pre>
<p>We can then refer to them at the top of our file:</p>
<pre><code>const { Configuration, OpenAIApi } = require("openai");

require("dotenv").config();

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY
});

const client = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN)
const twilioNumber = process.env.TWILIO_NUMBER;

const openai = new OpenAIApi(configuration);
</code></pre>
<p>We also probably want to include our prompt toward the top of the file (or in a secondary file):</p>
<pre><code>const prompt = `Add prompt here`;
</code></pre>
<p>Our POST endpoint will be responsible for handling:</p>
<ul>
<li>receiving a message from the user</li>
<li>sending the message stream to OpenAI</li>
<li>and then sending the response to the user.</li>
</ul>
<p>For this simple demo, I've created an in-memory JSON object that stores the user's phone number and message stream - but you will want to store this in an encrypted database on a production environment.</p>
<pre><code>app.post('/message', async (req, res) => {
  const { From, Body, ProfileName } = req.body;

  // create a new user if the number doesn't exist
  if (!messages[From]) {

    messages[From] = {
      name: ProfileName,
      chat: [prompt] // add the base prompt
    };
  }
</code></pre>
<p>The message is received by the POST endpoint. We then determine if we already have this user in our object. If we don't, we create them and add the initial prompt to our chat message stream. If we do, we retreive the chat history.</p>
<pre><code>  const currentMessageStream = messages[From].chat;

  // add the user message to our message stream
  currentMessageStream.push({"role": "user", "content": Body});
</code></pre>
<p>Now we have the message stream, we can push the user's new message to the array.</p>
<pre><code>  const response = await openai.createChatCompletion({
        model: "gpt-4",
        messages: currentMessageStream,
        max_tokens: 120,
        temperature: 0.5
      });
  const reply = await response.data.choices[0].message.content;

  currentMessageStream.push({"role": "system", "content": reply});
</code></pre>
<p>Next, we send the user's message to OpenAI and wait for a response, before recording that in our message stream.</p>
<pre><code>  return await client.messages.create({
      from: `whatsapp:${twilioNumber}`,
      body,
      to
    });

  res.send('')
});
</code></pre>
<p>Finally, we send the message back via WhatsApp to the user, and tell Express we are done with the request.</p>
<h2>Running our app</h2>
<p>With that complete, it's now time to run our Node app with <code>node start</code>. If you've been following along, you should have the app available on PORT 3000.</p>
<p>With the app up and running we need to configure Twilio's WhatsApp to use the service. The easiest way to do this locally is to use Ngrok (<code>brew install ngrok/ngrok/ngrok</code>). Once ngrok is installed you can run <code>ngrok http 3000</code> and copy the forwarding address.</p>
<p>Log in to Twilio and paste the forwarding address in to <a href="https://console.twilio.com/us1/develop/sms/try-it-out/whatsapp-learn?frameUrl=%2Fconsole%2Fsms%2Fwhatsapp%2Flearn%3Fx-target-region%3Dus1">Twilio's WhatsApp webhook</a></p>
<p>Within the same page, use the 'Sandbox participants' details at the bottom of the page to add the WhatsApp number, and then type and send the code as <code>join [code]</code>.</p>
<p>With these steps complete, you should now be able to chat to your very own WhatsApp chat bot :)</p>]]></content:encoded>
      <pubDate>Sun, 03 Sep 2023 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Onboarding like a Pro</title>
      <link>https://www.andismith.com/blogs/2023/06/onboarding-like-a-pro</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2023/06/onboarding-like-a-pro</guid>
      <description>How to get your team up to speed... fast!</description>
      <content:encoded><![CDATA[<p><strong>How to get your team up to speed.</strong></p>
<p>Imagine it's your first day at a new employer. You're equal parts excited, equal parts nervous. You turn up, get handed a laptop (hopefully) and get told to get on with it. So you sit down, and you turn on the laptop. Maybe set up your username and password if they haven't been done for you.</p>
<p>As a first install, you go to download our favourite browser. So you open Safari (sorry Safari you're not my favourite) and then you realise it... you don't have the WiFi code. You're sitting in a room full of people you've never met. You're meant to know what your doing, and you don't even have the WiFi code.</p>
<p>You pluck up the courage to get the code, log in to the WiFi and start downloading software. Everyone is busy dealing with whatever feature is running late and needs to be delivered yesterday. And you're sitting there again... any chance of some kind of onboarding? Or at least a onboarding Confluence page?</p>
<p>This has happened to me... I joined a company and there was no onboarding documentation whatsoever. And guess what? It took ages to get anyone up to speed on the systems! Hmmm.. I wonder why.</p>
<p>In my case, I made it my mission to make sure that there was good onboarding documentation. And I've made it my mission in every company since. Getting my team up to speed when they join is a top priority. So what can we do?</p>
<h2>Create a Slack welcome workflow</h2>
<p>If you're using Slack, you can use it's built-in Workflow builder to generate a message when new members join your Slack community. You can use this message to share important links with your new starters - such as the Code of Conduct and any useful onboarding documentation.</p>
<div class="post-image">
<img alt="My entry in who&#x27;s who" src="https://www.andismith.com/assets/blogs/2023/06/onboarding-like-a-pro/slack-onboarding.webp">
Setting up a Slack welcome message
</div>
<h2>Create a hub</h2>
<p>Start by having a central place where new starters can access all the information in one place. I find Confluence works really well for this. You can have pages with information about your systems and link out to documentation that lives elsewhere. You can also link out to important tools and repos. Great.</p>
<p>So what kind of pages should we have in this hub?</p>
<ul>
<li><strong>Who are you people?</strong> - The new starter is sitting in a room full of people they don't know. Or worse, they are at home wondering who all these people are. A 'meet the team' page is a great place to start introducing the who. I find it works best when there's a photo, the person's name, how to contact them and the days they are in the office.</li>
</ul>
<div class="post-image">
<img alt="My entry in who&#x27;s who" src="https://www.andismith.com/assets/blogs/2023/06/onboarding-like-a-pro/meet-me.webp">
My entry in who is who.
</div>
<ul>
<li>
<p><strong>Why are we doing this?</strong> - Context is everything and a new starter has none. How can they sign up to your mission if they don't know what it is? Depending on the age of the company, this could be one or two parts.</p>
<ul>
<li>I like to set up a 'Technical Strategy' which outlines the following: "Where we are"; "Where we are going" and "How we are going to get there".</li>
<li>If the company has a long history of technology changes or acquisitions, then it can also be useful to have a page that just shares some context on that. e.g. this service was named this because....; or this codebase was a result of the merger with....</li>
</ul>
</li>
<li>
<p><strong>Onboarding checklist</strong> - Tell the employee what they need to do to have a successful first day. Are there company policies to agree to? Is there particular software that everyone needs (Slack?)? Do they need to spend some time with the product so they understand it?</p>
</li>
<li>
<p><strong>Software checklist</strong> - Usually I'd also have a page that shares links out to what different disciplines need to install to get the product up and running on their machine. Include some 'troubleshooting gotchas' here to help out with the common stumbling blocks - and encourage new starters to add to it.</p>
</li>
<li>
<p><strong>Day 1 glossary</strong> - It's likely when your new starter joins, they will think everyone else is speaking a different language. Creating a day 1 glossary which covers some of the main terms people use can really help. It doesn't need to be a complete glossary, just anything to give your new starter a power-up on day one.</p>
</li>
<li>
<p><strong>Set some engineering responsibilties</strong> - With some things like housekeeping, you can be clear at the start on your expectations and therefore create a baseline. My personal recommendations are:</p>
<ul>
<li>Done is better than perfect</li>
<li>Always leave something better than you found it</li>
<li>Communication is critical (don't sit on a problem)</li>
<li>Ensure features are well-documented (this can be self-documented)</li>
<li>Don't get stuck in analysis paralysis</li>
<li>Update dependencies as we go</li>
<li>Don't be afraid of the unknown</li>
</ul>
</li>
<li>
<p><strong>Coding principles</strong> - Lastly, set some coding principles. What are the things you want the developer to keep in mind during their tenure in your team? My personal choices are:</p>
<ul>
<li>KISS - Keep it simple stupid</li>
<li>AHA - Avoid hasty abstractions (DRY with care)</li>
<li>YAGNI - You ain't gonna need it</li>
<li>Single Responsibility Principle</li>
<li>Loose Coupling Principle</li>
</ul>
</li>
</ul>
<h2>Assigning a buddy</h2>
<p>Even if you're the most prepared onboarder in the world, your new starter is going to have questions. Assign them a buddy for their first couple of weeks - a go to that they can speak to regardless of the question. It'll double up as allowing them to create a real bond with someone.</p>
<p>Don't forget to check-in yourself at the end of the week too - both with your new starter and their manager. Understanding what is working and what's not early on means you have a much better chance of course correction if needed, and getting to know what your new starter thinks of your team and processes if not.</p>
<h2>Defining culture</h2>
<p>Team culture is a very important part of any team. Getting it wrong can have a serious effect on everyone’s productivity - and their well-being too. Culture can't be dictated - it has to be a team effort. You can however, give your team some pointers on what they should be thinking about whilst working in the team. Things like:</p>
<ul>
<li>assume good intentions</li>
<li>be the support you would like to have yourself</li>
<li>don't hide or control information are good starts.</li>
</ul>
<p>With these techniques in your arsenal, you'll be onboarding new starters in no time!</p>]]></content:encoded>
      <pubDate>Fri, 23 Jun 2023 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Aligning Leadership Values</title>
      <link>https://www.andismith.com/blogs/2021/09/aligning-leadership-values</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2021/09/aligning-leadership-values</guid>
      <description>How do you align your leadership team when it feels like everyone is working in a silo?</description>
      <content:encoded><![CDATA[<p><strong>How do you align your leadership team when it feels like everyone is working in a silo?</strong></p>
<p>When I took over leading the UK &#x26; European team at LifeWorks, one of my first tasks was to ensure everyone on the team was aligned properly. Historically, there had been siloed disciplines and a bit of a blame culture. For example, we being are blocked by X team I don't know what they are doing.</p>
<p>I fully believe that to change the culture you need to start at the top and lead by example. I also believe that one person cannot dictate a culture by themselves, it needs to be something that everyone is willing to invest in.</p>
<p>So I sat down with my leadership team and a pile of post it notes and I asked them to write down on these post-it notes the values they thought were important to have within their teams.</p>
<p>After around 15 minutes, we started grouping the post-its in to similar categories, and then proceeded in a round of (dot voting)[https://www.nngroup.com/articles/dot-voting/] to establish which values we wanted to bring forward. We then took the top six values and wrote up what they meant.</p>
<p>Once we had agreed our values, it was important for us to be able to keep ourselves and each other honest on them. So in our fortnightly team lead meetings, I asked everyone to come with two "awards" and we would go round the room at the start of the session and share our picks.</p>
<p>The first was an award to one person in the leadership team who had demonstrating one of these value, and explain why, to ensure we hold others accountable.</p>
<p>The second was award to ourselves for demonstrating one of the values, and explain why, to ensure we hold ourselves accountable.</p>
<p>It felt a bit strange at first, handing out pretend awards to each other - but it got us in the mindset for how we wanted to interact with each other. Once the teams were working better with each other we reduced the frequency - but at that point, the values were embedded in leaderships mind and collaboration and culture massively improved.</p>
<p>Here are the values we came up with:</p>
<ul>
<li>
<p><strong>Accountability</strong></p>
<ul>
<li>Being responsible for your actions</li>
<li>Being responsible for the actions of your team</li>
<li>Don't blame - fix, learn and move on</li>
</ul>
</li>
<li>
<p><strong>Collaboration</strong></p>
<ul>
<li>Working together</li>
<li>Being one team</li>
<li>Keeping integrity as a team</li>
</ul>
</li>
<li>
<p><strong>Empowerment</strong></p>
<ul>
<li>Empower our teams to make their own decisions</li>
<li>Empower our teams to succeed</li>
<li>Empower and challenge our teams to be better versions of themselves</li>
</ul>
</li>
<li>
<p><strong>Transparency</strong></p>
<ul>
<li>Not hiding or controlling information</li>
<li>Better communication</li>
<li>Understanding others perspectives and listening to the team</li>
<li>Don't ignore problems - we might not be able to fix something right now, but we should record and be aware of the issue</li>
</ul>
</li>
<li>
<p><strong>Trust</strong></p>
<ul>
<li>Be the support you would like to have</li>
<li>Live what we preach as a business</li>
<li>Belief that other have good intentions and work to the best of their abilities</li>
</ul>
</li>
<li>
<p><strong>Vision</strong></p>
<ul>
<li>A compelling and clear reason to take your team in a certain direction</li>
<li>One common vision within the business</li>
<li>Understand our business</li>
<li>Make a difference</li>
</ul>
</li>
</ul>]]></content:encoded>
      <pubDate>Wed, 08 Sep 2021 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>The Start of Something New</title>
      <link>https://www.andismith.com/blogs/2018/01/the-start-of-something-new</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2018/01/the-start-of-something-new</guid>
      <description>After almost 11 years, I am moving on from AKQA to pastures new.</description>
      <content:encoded><![CDATA[<p>After almost 11 years, I am moving on from AKQA to pastures new.</p>
<p>It would seem impossible to 24 year old Andi that I would stay at the same company for almost a third of my life. After a couple of years at two previous jobs I found myself sitting in an interview to be a web developer at AKQA. From the interview I could tell that this company would challenge me in ways I had never been challenged before - with an unrivaled design team, a drive for innovation and a fine eye for detail AKQA has responsible for producing a wealth of incredible experiences.</p>
<p>It's been a wild adventure working for one of the world's most respected and innovative agencies. I worked my way up the ranks and it's been my absolute pleasure to lead the web development team for the last few years.</p>
<p>We've gone through many changes in my time at the agency. When I started designers used to throw fixed sized PSDs over the fence to developers who would sit in a different part of the building trying to make transparent PNGs work for rounded corners in IE6. Nowadays, we have a wealth of different device resolutions and a collaborative environment where Creative, UX and developers work together with not a rounded corner in sight (now it's easy to code them).</p>
<p>I feel so incredibly lucky to have worked on so many projects for so many different respected clients on such a wide variety of projects.</p>
<ul>
<li>I've helped build massive digital platforms, car configurators, online games, competitions and many smaller campaign pieces.</li>
<li>We've built web applications, mobile applications, hybrid applications, chat bots, Alexa skills and retail experiences.</li>
<li>We've built car configurators, shoe configurators, running configurators and front end build configurators.</li>
<li>We've used content management solutions of all scales from experience manager AEM to CMS solutions Sitecore, Wordpress and Drupal to headless CMS with Contentful and good old plain JSON.</li>
<li>I've worked first hand with technical teams from other AKQA offices, BMW, Nike, Delta and Facebook.</li>
<li>The work I've completed has won many awards including Cannes Gold Lions, Webby awards and a few FWA Site of the Days.</li>
<li>And I've got to see parts of the world I never thought I would when I travelled to Amsterdam, San Francisco, Munich, Zurich, Boston and Atlanta to work with clients on a huge variety of solutions.</li>
</ul>
<p>I have many, many fond memories of working at AKQA, but the highlight has to be when <a href="https://www.campaignlive.co.uk/article/heineken-rolls-starplayer-football-game/1067169">we launched Heineken Star Player at the Emirates Stadium</a> to a room full of the press. To see something we built used and loved by a room full of people was incredible. Walking around the room and watching football fanatics and sports pundits enjoying the game alongside the beautiful game was an unforgettable experience.</p>
<p>And with all those memories I leave with a heavy heart. Although my journey at AKQA has come to an end, AKQA will always be a part of me. I have learnt and gained so much from my time at the company. I feel so incredibly privileged. Don't get me wrong - it has been hard at times. But it's also been very rewarding.</p>
<p>AKQA are currently hiring - so if you'd like to experience some of the things I mentioned above and work with a great team, <a href="https://www.akqa.com/careers/job/?id=934161">please do get in touch with them</a>.</p>
<p>I can't wait to share with you what I'm up to next very soon. To the next adventure!</p>]]></content:encoded>
      <pubDate>Fri, 12 Jan 2018 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>The Quest for the Perfect Workflow</title>
      <link>https://www.andismith.com/blogs/2014/05/the-perfect-workflow</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2014/05/the-perfect-workflow</guid>
      <description>Getting the right tools for the job.</description>
      <content:encoded><![CDATA[<p><strong>Getting the right tools for the job.</strong></p>
<p>Today I spoke at jQuery UK on my quest to find the Perfect Workflow.</p>
<p>Unfortunately, we couldn't fit everyone who wanted to come to the talk in the room. If you missed the talk, I've recorded this screencast. There's more information below the video.</p>
<div class="post-image">
<iframe src="//player.vimeo.com/video/95625156" width="500" height="313" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen></iframe>
</div>
<p>Part of my job as a Technical Architect is to make sure that projects start with the right set of base tools and the right workflow. But during my quest I realised that there is no such thing as a perfect workflow for everybody, because everyone’s requirements are different. We all use different tools and have different coding styles. And a workflow that enforces any rules that conflict with these requirements is going to get cursed from start to finish.</p>
<p>So my quest morphed in to looking in to ways how we can improve our workflow.</p>
<p>I started by splitting our workflow in to three sections to make sure that we are using the right tasks for the right part of project:</p>
<p>Setup, Develop and Build</p>
<ul>
<li>The Setup stage should focus on the foundations of our project</li>
<li>The Develop stage should focus on performance, so that you can make changes to your site and see the results immediately.</li>
<li>The Build stage should focus on file size optimisations, concatenation, minification .etc.</li>
</ul>
<div class="post-image">
<iframe src="http://www.slideshare.net/slideshow/embed_code/34754321" width="476" height="400" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div>
<p>The tasks and tools I covered were:</p>
<h3>Setup</h3>
<ul>
<li><a href="http://gruntjs.com/">GruntJS</a></li>
<li><a href="http://gulpjs.com/">GulpJS</a></li>
<li><a href="http://yeoman.io/">Yeoman</a></li>
<li>Auto load tasks
<ul>
<li><a href="https://github.com/sindresorhus/load-grunt-tasks">Grunt</a> - <code>npm install load-grunt-tasks</code></li>
<li><a href="https://github.com/jackfranklin/gulp-load-plugins">Gulp</a> - <code>npm install gulp-load-plugins</code></li>
</ul>
</li>
<li>Start a local server
<ul>
<li><a href="https://github.com/gruntjs/grunt-contrib-connect">Grunt</a> - <code>npm install grunt-contrib-connect</code></li>
<li><a href="https://github.com/avevlad/gulp-connect">Gulp</a> - <code>npm install gulp-connect</code></li>
</ul>
</li>
<li>Workflow Performance
<ul>
<li><a href="https://github.com/sindresorhus/time-grunt">Grunt</a> - <code>npm install time-grunt</code></li>
<li><a href="https://github.com/hughsk/gulp-duration">Gulp</a> - <code>npm install gulp-duration</code></li>
</ul>
</li>
<li><a href="https://github.com/sindresorhus/grunt-concurrent">Make Grunt Faster</a> - <code>npm install grunt-concurrent</code></li>
</ul>
<h3>Develop</h3>
<ul>
<li>Use Autoprefixer
<ul>
<li><a href="https://github.com/nDmitry/grunt-autoprefixer">Grunt</a> - <code>npm install grunt-autoprefixer</code></li>
<li><a href="https://github.com/Metrime/gulp-autoprefixer">Gulp</a> - <code>npm install gulp-autoprefixer</code></li>
</ul>
</li>
<li>Watch &#x26; LiveReload
<ul>
<li><a href="https://github.com/gruntjs/grunt-contrib-watch">Grunt</a> - <code>npm install grunt-contrib-watch</code></li>
<li>Gulp - <code>gulp.watch</code></li>
<li>LiveReload extensions:
<ul>
<li><a href="http://bit.ly/1ojCxVq">Chrome</a></li>
<li><a href="http://bit.ly/1hs7yBT">Firefox</a></li>
<li><a href="http://bit.ly/1sbwfcC">Safari</a></li>
</ul>
</li>
</ul>
</li>
<li>Compile Changed Files
<ul>
<li><a href="https://github.com/tschaub/grunt-newer">Grunt</a> - <code>npm install grunt-newer</code></li>
<li><a href="https://github.com/sindresorhus/gulp-changed">Gulp</a> - <code>npm install gulp-changed</code></li>
</ul>
</li>
<li>Live Editing Our Files</li>
</ul>
<h3>Build</h3>
<ul>
<li>Compile CSS/JS and replace references in HTML.
<ul>
<li><a href="https://github.com/yeoman/grunt-usemin">Grunt</a> - <code>npm install grunt-usemin</code></li>
<li><a href="https://github.com/zont/gulp-usemin">Gulp</a> - <code>npm install gulp-usemin</code></li>
</ul>
</li>
<li>Combine Media Queries
<ul>
<li><a href="https://github.com/buildingblocks/grunt-combine-media-queries">Grunt</a> - <code>npm install grunt-combine-media-queries</code></li>
<li><a href="https://github.com/konitter/gulp-combine-media-queries">Gulp</a> - <code>npm install gulp-combine-media-queries</code></li>
</ul>
</li>
<li>UnCSS
<ul>
<li><a href="https://github.com/addyosmani/grunt-uncss">Grunt</a> - <code>npm install grunt-uncss</code></li>
<li><a href="https://github.com/ben-eb/gulp-uncss">Gulp</a> - <code>npm install gulp-uncss</code></li>
</ul>
</li>
<li>Streamline Modernizr
<ul>
<li><a href="https://github.com/Modernizr/grunt-modernizr">Grunt</a> - <code>npm install grunt-modernizr</code></li>
<li><a href="https://github.com/doctyper/gulp-modernizr">Gulp</a> - <code>npm install gulp-modernizr</code></li>
</ul>
</li>
<li>Minify Images
<ul>
<li><a href="https://github.com/gruntjs/grunt-contrib-imagemin">Grunt</a> - <code>npm install grunt-imagemin</code></li>
<li><a href="https://github.com/sindresorhus/gulp-imagemin">Gulp</a> - <code>npm install gulp-imagemin</code></li>
</ul>
</li>
<li>Compress Files
<ul>
<li><a href="https://github.com/gruntjs/grunt-contrib-compress">Grunt</a> - <code>npm install grunt-contrib-compress</code></li>
<li><a href="https://github.com/jstuckey/gulp-gzip">Gulp</a> - <code>npm install gulp-gzip</code></li>
</ul>
</li>
<li>Compress Files with Zopfli
<ul>
<li><a href="https://github.com/mathiasbynens/grunt-zopfli">Grunt</a> - <code>npm install grunt-zopfli</code></li>
<li><a href="https://github.com/romeovs/gulp-zopfli">Gulp</a> - <code>npm install gulp-zopfli</code></li>
</ul>
</li>
<li>Shrinkwrap - <code>npm shrinkwrap</code></li>
</ul>
<p>Thanks to everyone who came along to the talk today, I hope you found it useful! Please let me know your thoughts in the comments below, or <a href="http://www.twitter.com/andismith">@andismith</a> on Twitter.</p>]]></content:encoded>
      <pubDate>Fri, 16 May 2014 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Creating a List of Posts in Assemble</title>
      <link>https://www.andismith.com/blogs/2014/02/creating-a-list-of-posts-in-assemble</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2014/02/creating-a-list-of-posts-in-assemble</guid>
      <description>In the previous post, I showed how to get started with Assemble. Now we have content, let&apos;s look at how we can create a list of posts.</description>
      <content:encoded><![CDATA[<p>In <a href="http://localhost:3000/blog/2014/02/getting-started-with-assemble/">the previous post</a>, I showed how to get started with Assemble. Now we have created a page and a post, it would be great to display our posts on our homepage. We could manually list our posts within our index page, or we could store them in a data file somewhere. But it would be much better if we could list them dynamically.</p>
<h2>Creating a list of posts</h2>
<p>Assemble allows us to specify lists of posts and/or pages as collections, which we can then use to loop through and get our content. To specify a collection, we need to edit our <code>Gruntfile.js</code> and add a collections object to our posts:</p>
<pre><code class="language-javascript">options: {
  collections: [{
    name: 'post',
    sortby: 'posted',
    sortorder: 'descending'
  }],
  ...
</code></pre>
<p>This collection looks for a YAML parameter of <code>post</code> to choose which posts to contain, so we need to add a new value to our file <code>src/content/blog/first-post.md</code>:</p>
<pre><code class="language-html">---
title: "First!"
posted: 2014-01-28
post: true
---

This is my first post!
</code></pre>
<p>This will allow us to identify our blog posts as posts, and keep our pages out of the collection.</p>
<p>With our posts marked up we need to create a loop for our collection. Open up <code>src/bonnet/layouts/page.hbs</code> and include the following code after our closing {{/markdown}} tag:</p>
<pre><code class="language-html">&#x26;lt;ul> {{#withSort post 'data.posted'}} {{#each this.pages }} &#x26;lt;li>{{
this.data.title }}&#x26;lt;/li> {{/each}} {{/withSort}} &#x26;lt;/ul>
</code></pre>
<p>Here we are telling Assemble that with our collection 'post' ordered by the date posted, we want to loop through the collection and output the page title.</p>
<p>If you re-run <code>grunt</code> from the Terminal/Command Prompt you should now see a list of a single item on all your pages. Let's add another post at <code>src/content/blog/second-post.md</code>:</p>
<pre><code class="language-html">---
title: "Second!"
posted: 2014-01-30
post: true
---

This is my second post!
</code></pre>
<p>If we re-run <code>grunt</code> again we should see two items in our list. Horrah!</p>
<h3>A list with links</h3>
<p>A list is great, but it would be great if users could click our list to go to a blog post.</p>
<p>In an ideal world, we would be able to output the path of the post with {{ this.dest }}, but this path includes the 'dist/' folder and to my knowledge there is no way to remove this through Assemble. Instead, we need to create our first Handlebars helper.</p>
<p>Create a new folder called <code>helpers</code> in your <code>bonnet</code> directory and add a file called <code>helpers.js</code>. Inside, we'll create our first helper:</p>
<pre><code class="language-javascript">module.exports.register = function (Handlebars, options) {
  "use strict";

  Handlebars.registerHelper(
    "replaceStr",
    function (haystack, needle, replacement) {
      if (haystack &#x26;&#x26; needle) {
        return haystack.replace(needle, replacement);
      } else {
        return "";
      }
    }
  );
};
</code></pre>
<p>Our helper is called <code>replaceStr</code> and will search <code>haystack</code> for <code>needle</code> and replace it with <code>replacement</code>.</p>
<p>We need to tell Assemble where our helpers live, so in our Gruntfile add the following line to Assemble's options:</p>
<pre><code class="language-javascript">helpers: './src/bonnet/helpers/**/*.js',
</code></pre>
<p>With these parts completed, we can now add the blog URL to our code:</p>
<pre><code class="language-html">&#x26;lt;li>&#x26;lt;a href="{{ replaceStr this.dest 'dist' '' }}">{{ this.data.title
}}&#x26;lt;/a>&#x26;lt;/li>
</code></pre>
<p>If we re-run <code>grunt</code> now, our index page will now link to our post pages; and our post pages will link to each other. And thus, we have now created a list of posts for our blog that sits on each page.</p>
<p>If you've not been coding, you can access our progress so far by cloning the example repo:</p>
<pre><code class="language-bash">git clone https://github.com/andismith/assemble-blog-template.git
git checkout v2
npm install
</code></pre>
<h2>Coming Up</h2>
<p>I'll be posting a number of shorter tutorials showing how to add extra functionality to your blog starting with adding categories and pagination.</p>
<p>Stay tuned!</p>]]></content:encoded>
      <pubDate>Thu, 27 Feb 2014 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Getting Started With Assemble</title>
      <link>https://www.andismith.com/blogs/2014/02/getting-started-with-assemble</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2014/02/getting-started-with-assemble</guid>
      <description>Want to create a static site blog with Assemble? You&apos;ve come to the right place. This in-depth tutorial will get you started with creating a blog in Assemble!</description>
      <content:encoded><![CDATA[<p>The site you are visiting right now was created with <a href="http://assemble.io/">Assemble</a>, a static site generator for the Grunt task runner. This blog is part one of a multi part series looking at how to create your own blog with this tool.</p>
<p>If you're unsure what a static site is, that's ok. It's a site where the pages are compiled, constructed and stored before the user requests them, usually at build time. So when the user hits the page, they are just requesting the pre-generated HTML output. Static sites can still use JavaScript for client side functionality, so AJAX GET requests, animations and interactivity are still all possible - but there is no dynamic data.</p>
<p>With the redesign of my blog, I wanted to move from <a href="http://wordpress.org/">Wordpress</a> to a static site so I could simplify my setup and so I could write my posts in <a href="http://daringfireball.net/projects/markdown/">Markdown</a>. Wordpress is a great blog CMS, but it can be difficult to insert code in to blog posts. In Markdown, I have full control over how each of my posts look.</p>
<h2>Assemble vs Jekyll</h2>
<p>Originally when I began the redesign I used <a href="http://jekyllrb.com/">Jekyll</a> - a static site generator from the creators of Github. But I found Jekyll had a couple of things I wasn't happy about.</p>
<p>The main reason was because of a couple of limitations in Jekyll. I wanted to have multiple sets/collections of posts on my site - a set of posts about my blog; a set of posts for my portfolio; and another set of posts; and I wanted to use the lists of posts on multiple pages. I also wanted to have the freedom to re-order them. In Jekyll you can only have one set of posts and the filenames must be prefixed by a date (which is how they are ordered). Happily, in Assemble you can have as many as you like in any order you require.</p>
<blockquote>
<p>In Jekyll you can only have one set of posts in descending date order. In Assemble you can have multiple in any order.</p>
</blockquote>
<p>The JavaScript purist in me wanted to run everything in JavaScript, and Jekyll has a dependency on Ruby. At the time I was having to run Grunt and then run Jekyll to build my site. I really wanted to combine these two processes in to one and use <code>grunt-connect</code> and <code>grunt-watch</code> to ensure immediate generation of new content. There is now <a href="https://github.com/dannygarcia/grunt-jekyll">a Grunt task for generating Jekyll sites</a> which solves this issue.</p>
<img src="https://www.andismith.com/assets/blogs/2014/02/getting-started-with-assemble/assemble.gif" alt="Assemble Website">
<p>Assemble is still fairly new and features are being added as they are requested. Being on the very bleeding edge can be a little frustrating when something that should work doesn't and I found myself having to come back to Assemble every few weeks to see if it was now possible for me to do what I wanted to do with it. Happily, Assemble is in active development and raising an issue in Github generally results in a response and the right functionality being added.</p>
<blockquote>
<p>Assemble is in active development and raising an issue in Github generally results in functionality being added.</p>
</blockquote>
<p>There's lots of documentation for Assemble but what you want is often hidden away so deep you have to know exactly what to look for. The documentation and example repositories never really cover the use case of something like creating a normal blog (hence these posts) - there is an example Assemble blog repository but it requires both the post files and <a href="https://github.com/upstage/assemble-blog-theme/blob/master/data/pages.json">a JSON file with a list of the posts</a> which means you have to maintain two areas to write a new post. I really didn't want to do this.</p>
<p>So is Assemble right for you? It depends on your use case. If you just want one stream of posts and to be up and running in a few hours then I'd probably recommend Jekyll. If you're comfortable with JavaScript; want more flexibility and feel like you'd be happy writing additional helpers for the <a href="http://handlebarsjs.com/">Handlebars templating language</a> then Assemble may be what you're looking for.</p>
<blockquote>
<p>If you want more flexibility than Jekyll, then Assemble may be what you're looking for.</p>
</blockquote>
<p>This series of posts will aim to guide you through how to create a site not dissimilar to the one here. By the time we've finished the series, you'll have a site that includes:</p>
<ul>
<li>A homepage.</li>
<li>A blog section (and the knowledge of how to create new sections).</li>
<li>Categories.</li>
<li>Pagination.</li>
<li>The ability to add custom CSS and JavaScript on a per blog basis.</li>
<li>Comments from Disqus and code syntax highlighting from Prism.</li>
<li>A RSS feed.</li>
<li>A contact form.</li>
</ul>
<h2>How Assemble works</h2>
<p>Before we start building in Assemble it is important to know how it works.</p>
<p>A page is constructed from the data we make available to it. It takes the Markdown content and YAML data we specify in our Markdown file and combines it with any extra data files we have specified, together with a number of other useful data pieces such as the filename.</p>
<p>This data combination creates a JavaScript object that we can output and manipulate on our page using Handlebars templating.</p>
<p>Every page requires a layout, which acts as a container. Inside the layout, we can include multiple partials that include segments of our page - for example, we may have our site navigation in a partial, so it can be included in multiple layouts or even at the top and bottom of our site. Partials can be included within partials.</p>
<img src="https://www.andismith.com/assets/blogs/2014/02/getting-started-with-assemble/assemble-structure.png" alt="How Assemble works">
<p>As shown in this diagram, the layout forms the shell of the page. Inside, partials contain modules of code such as the header, navigation and sidebar. The sidebar contains partials itself such as social, latest and categories. The page content (or the post content) is included inside the post partial.</p>
<p>With that in mind, let's begin!</p>
<h2>Starting With Assemble</h2>
<p>This tutorial assumes you already have some knowledge of how Grunt works, and that <a href="http://gruntjs.com/getting-started">you have already installed <code>grunt-cli</code></a>. If you don't, here's a couple of articles to take a look at:</p>
<ul>
<li>For the beginner afraid of the command line, Chris Coyier's <a href="http://24ways.org/2013/grunt-is-not-weird-and-hard/">excellent Grunt tutorial for 24 ways</a> is a great place to start.</li>
<li>My tutorial on <a href="http://12devsofxmas.co.uk/2014/01/day-10-maintaining-a-better-workflow-with-grunt/">maintaining a better workflow with Grunt for 12 Devs of Xmas</a> is also worth a read.</li>
</ul>
<h3>Creating an empty project in Grunt</h3>
<p>Assuming you don't already have an empty project folder, open up Windows Command Prompt or Mac Terminal and navigate to your normal workspace directory. Create a new project folder using <code>mkdir</code>:</p>
<pre><code class="language-bash">mkdir assemble-blog
</code></pre>
<p>Move in to the <code>assemble-blog</code> directory and run <code>npm init</code> to create an initial <code>package.json</code> file:</p>
<pre><code class="language-bash">cd assemble-blog
npm init
</code></pre>
<p>We'll need to tell our project we want to use Grunt by including it in our <code>package.json</code>, so run the following command to add Grunt:</p>
<pre><code class="language-bash">npm install grunt --save-dev
</code></pre>
<p>We will also want to use <code>grunt-contrib-connect</code>, which is a task for running a local server with Grunt. Assemble doesn't require a server to run (it is a static site solution afterall), it reduces complications later.</p>
<pre><code class="language-bash">npm install grunt-contrib-connect --save-dev
</code></pre>
<p>Next, we'll want to create a file called <code>Gruntfile.js</code> within our project directory, with the following contents:</p>
<pre><code class="language-javascript">module.exports = function (grunt) {
  "use strict";

  grunt.initConfig({
    pkg: grunt.file.readJSON("./package.json"),

    connect: {
      dev: {
        options: {
          port: 8000,
          base: "./dist/",
        },
      },
    },
  });

  /* load every plugin in package.json */
  grunt.loadNpmTasks("grunt-contrib-connect");

  /* grunt tasks */
  grunt.registerTask("default", ["connect"]);
};
</code></pre>
<h3>Installing Assemble</h3>
<p>Now we have a base project directory with <code>grunt</code> and <code>grunt-contrib-connect</code> installed, run the following command:</p>
<pre><code class="language-bash">npm install assemble --save-dev
</code></pre>
<p>Once Assemble has been installed, we need to create our project structure. Our directory layout is completely configurable so it can match your requirements, but I found the following works well:</p>
<div class="post-image">
  <img src="https://www.andismith.com/assets/blogs/2014/02/getting-started-with-assemble/directory.gif" alt="Directory structure">
</div>
<!--
* dist
* src
  * bonnet
    * layouts
    * partials
  * content
    * _pages
    * blog
  * assets
* Gruntfile.js
* package.json
* README.md
-->
<p>I like to group the parts that compile the pages in to a folder called 'bonnet' (as in car bonnet - let's look under the bonnet to see how the car works), but you could call it anything.</p>
<p>I also include a folder called <code>_pages</code> which is where root level page content will live to stop the <code>content</code> directory getting cluttered.</p>
<p>Rather than manually create this folder and file structure, you can <a href="/downloads/assemble-structure.zip">download a zip of the structure here</a>.</p>
<p>We need to tell the Assemble task about our structure, so let's open up our Gruntfile.js and make some changes.</p>
<p>We'll need to load the Assemble task and include a section for Assemble within our <code>grunt.initConfig</code> object. At it's simplest, and if you've followed the structure I've suggested above, your Grunt file should look like this:</p>
<pre><code class="language-javascript">module.exports = function (grunt) {
  'use strict';

  grunt.initConfig({
    pkg: grunt.file.readJSON('./package.json'),

    connect: {
      dev: {
        options: {
          port: 8000,
          base: './dist/'
        }
      }
    },

    assemble: {
      options: {
        layout: 'page.hbs',
        layoutdir: './src/bonnet/layouts/',
        partials: './src/bonnet/partials/**/*.hbs'
      },
      posts: {
        files: [{
          cwd: './src/content/',
          dest: './dist/'
          expand: true,
          src: ['**/*.hbs', '!_pages/**/*.hbs']
        }, {
          cwd: './src/content/_pages/',
          dest: './dist/'
          expand: true,
          src: '**/*.hbs'
        }]
      }
    }
  });

  /* load every plugin in package.json */
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('assemble');

  /* grunt tasks */
  grunt.registerTask('default', ['assemble', 'connect']);

};
</code></pre>
<p>Like most Grunt tasks, Assembles accepts an <code>options</code> object and any number of tasks. Here we've created a task called 'posts' which will (surprise, surprise) process all our posts.</p>
<p>Within our <code>options</code> section, we have added paths to match the directory structure we created earlier and have included the <a href="https://github.com/assemble/assemble#options">settings Assemble requires to get up and running</a>.</p>
<ul>
<li><code>layoutdir</code>: Should be set to the path where any layouts we create will be located.</li>
<li><code>layout</code>: This is the default layout that should be used for our files, the file will be suffixed with <code>.hbs</code>.</li>
<li><code>partials</code>: Should be set to the path where any partial layouts we create will be located.</li>
</ul>
<p>Our posts task files object contains two arrays with file settings.</p>
<p>The first tells Assemble to look for all our content pages (with the extension .hbs), which is any file not in the <code>_pages</code> directory. Once these files are processed they will be placed in the <code>./dist/</code> folder.</p>
<p>The second tells Assemble to compile the contents of the <code>_pages</code> directory. We also want these files to be placed in the <code>./dist</code> folder, so we include the <code>_pages</code> page in our <code>cwd</code>.</p>
<p>Now we've set up Grunt, it's time to add some files!</p>
<h2>Creating our base site</h2>
<p>To get us started, let's create some files that will construct our site:</p>
<h3>The layout</h3>
<p><code>src/bonnet/layouts/page.hbs</code> contains three partials - the site header, the page content and the site footer. We include a partial in Assemble using <code>{{>partial-name}}</code>.</p>
<pre><code class="language-html">{{>site-header}} &#x26;lt;div class="page-content"> &#x26;lt;h2>{{ this.page.title
}}&#x26;lt;/h2> {{#markdown}} {{> body}} {{/markdown}} &#x26;lt;/div> {{>site-footer}}
</code></pre>
<h3>The partials</h3>
<p><code>src/bonnet/partials/site-header.hbs</code> contains the top of our HTML document, and it is just static HTML (for the moment):</p>
<pre><code class="language-html">&#x26;lt;!DOCTYPE html> &#x26;lt;html> &#x26;lt;head> &#x26;lt;meta charset="utf-8" /> &#x26;lt;title>My
Site&#x26;lt;/title> &#x26;lt;/head> &#x26;lt;body> &#x26;lt;div class="header"> &#x26;lt;h1>My
Site&#x26;lt;/h1> &#x26;lt;/div>
</code></pre>
<p>Finally, we need to create <code>src/bonnet/partials/site-footer.hbs</code> which contains the bottom of our HTML document, and again is just static HTML:</p>
<pre><code class="language-html">&#x26;lt;/body> &#x26;lt;/html>
</code></pre>
<p>With our site building blocks completed, it's time to add some content!</p>
<h3>The content</h3>
<p>To start with, we will add two content files. The first is the homepage of the site and the second will be a test blog post. Our content files contain YAML (Yet Another Markup Language) for page data before using traditional Markdown for their content. If you've not used YAML before don't panic, it should be easy to see how it works from the examples below.</p>
<p><code>src/content/_pages/index.hbs</code> will look like this:</p>
<pre><code class="language-html">---
title: "Welcome"
posted: 2014-01-28
---

Welcome to my site, built in Assemble!
</code></pre>
<p>Our first post located at <code>src/content/blog/first-post.md</code> will look like this:</p>
<pre><code class="language-html">---
title: "First!"
posted: 2014-01-28
---

This is my first post!
</code></pre>
<p>With all this complete, it's time to see what it looks like when generated. Back on your Terminal/Command Prompt pointing at your project directory type:</p>
<pre><code class="language-javascript">grunt assemble</code></pre>
<p>Let's see what we've got!</p>
<p>If it has worked correctly, Assemble should generate two pages - <code>dist/blog/first-post.html</code> and <code>dist/index.html</code>. And when we open up our site (at <a href="http://localhost:8000/">http://localhost:8000/</a>) in our browser, we should get this:</p>
<div class="post-image">
  <img src="https://www.andismith.com/assets/blogs/2014/02/getting-started-with-assemble/first-site.gif" alt="First site">
</div>
<p>Beautiful!</p>
<p>If you've not been coding, you can retreive our progress so far from Git by running:</p>
<pre><code class="language-javascript">git clone https://github.com/andismith/assemble-blog-template.git
git checkout v1
npm install
</code></pre>
<h2>Coming Up</h2>
<p>Now we've got up and running with the basics, we're ready to look in to bigger and better things. The next post covers creating a list of posts in Assemble - and <a href="/blog/2014/02/creating-a-list-of-posts-in-assemble">is available right now</a>!</p>
<p>Stay tuned!</p>]]></content:encoded>
      <pubDate>Wed, 26 Feb 2014 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Shrinkwrap Your Dependencies</title>
      <link>https://www.andismith.com/blogs/2014/02/shrinkwrap-your-dependencies</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2014/02/shrinkwrap-your-dependencies</guid>
      <description>Front end development has evolved over the last couple of years thanks to `npm` popularised by task runners such as Grunt and Gulp.</description>
      <content:encoded><![CDATA[<p>Front end development has evolved over the last couple of years thanks to <code>npm</code> popularised by task runners such as Grunt and Gulp.</p>
<p>Thanks to our package.json files, it's easy for another developer to get set up on our project in seconds by typing <code>npm install</code>. But what happens when some time has passed and your project dependencies have moved on? A new version of a package may introduce a new bug, or completely change its functionality altogether.</p>
<p>It's possible to have <a href="https://npmjs.org/doc/misc/semver.html">some control</a> over dependency versions with the version numbers you put in your package.json file, but it's extremely difficult to have control over the version numbers of your dependencies dependencies.</p>
<p>For this reason, it's a good idea to run npm's shrinkwrap feature to lock down the versions of dependencies you are using once you have reached a stable point in development.</p>
<p>To do this, open up Command Prompt or Terminal and navigate to your project's root folder. Then type:</p>
<pre><code class="language-javascript">npm shrinkwrap</code></pre>
<p>Calling <code>npm shrinkwrap</code> scans your <code>node_modules</code> folder and creates a <code>npm-shrinkwrap.json</code> file that contains a complete breakdown of all the dependencies (and versions) of your project, which npm will then use as its reference when creating a new install.</p>
<p><a href="https://npmjs.org/doc/shrinkwrap.html">You can read more about shrinkwrap in the npm documentation</a>.</p>]]></content:encoded>
      <pubDate>Wed, 05 Feb 2014 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>A Look Back at 2013</title>
      <link>https://www.andismith.com/blogs/2014/01/a-look-back-at-2013</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2014/01/a-look-back-at-2013</guid>
      <description>2013 was an eventful year for me both professionally and personally. I worked on a multitude of different projects in a variety of clients in a number of countries.</description>
      <content:encoded><![CDATA[<p>2013 has come and gone, and it was an eventful year for me both professionally
and personally. Here's a short summary of what I've been up to:</p>
<p><strong>January</strong> had only just begun when I was flown over to San Jose to work at Facebook for a two week time-intensive project for one of our clients. The brief was to come up with a tightly integrated social experience for a fitness application.</p>
<blockquote>
<p>January had only just begun when I was flown<br>over to San Jose to work at Facebook.</p>
</blockquote>
<p>I learnt so much about how social works from the team at Facebook as well as gaining an insight in to their philosophies on shipping software, it was truly inspiring. I think we were also able to teach the Facebook developers a few things about their API coming from the perspective of users of the API.</p>
<div class="post-image">
<img alt="Facebook" src="https://www.andismith.com/assets/blogs/2014/01/a-look-back-at-2013/facebook.jpg">
</div>
<p>In <strong>March</strong>, I got married to the beautiful Amy Adams (now Smith) after 7
years together. We got married in a manor house in Dorking. It tried to snow
four times during the day, but it didn't put a dampener on our special event.
We went to Mexico for our honeymoon in April.</p>
<div class="post-image">
<img alt="Wedding" src="https://www.andismith.com/assets/blogs/2014/01/a-look-back-at-2013/wedding.jpg">
</div>
<p>In <strong>May</strong>, I launched <a href="http://devtoolsecrets.com/">Dev Tool Secrets</a> - a site
dedicated to exposing more about the developer tools included within each
browser. The site is hosted on Github so anyone can add contributions, I will
be adding more tips during the next few months. I went on to do a number of
presentations during June and July at London meet ups on developer tool
secrets.</p>
<p>My <a href="http://andismith.github.io/caniuse-widget/">Can I Use widget</a> which allows
bloggers to list browser compatibility when discussing features also gained a
lot of traction during May thanks to a blog post from Bruce Lawson, and is now
featured on several sites (such as <a href="http://css3clickchart.com/">CSS3 Click
Chart</a>) as well as in print on the "projects"
front page of NET Magazine!</p>
<p>In <strong>July</strong> I launched [Grunt Responsive Images](https://github.com/andismith
/grunt-responsive-images), a Grunt task that resizes images based on a number
of configurable parameters, especially useful for responsive websites. The
plugin came about when I was architecting a solution for using responsive
images uploaded by content authors. Grunt seemed like the perfect solution for
this problem, and it only took a couple of evenings to get the first version
of the plugin up and running.</p>
<p>In July, I also attended <a href="http://hacked.io/">Hacked.io</a>, a 24 hackathon
in London's O2 arena. Working alongside Abid Dinn, Chris Marsh and Tom Maton we
produced a music quiz that hooked in to the Nokia Music API. We won 2 prizes - the
Nokia Music API challenge, and one of the Heroku challenges. It was an awesome weekend.</p>
<blockquote>
<p>In July, I also attended Hacked.io, a 24 hour hackathon in London's O2 arena. We won 2 prizes.</p>
</blockquote>
<p>In <strong>September</strong>, I wrote an 7 page article for NET Magazine on browser
developer tools which became the cover article. It was exciting to see
my article displayed proudly on the shelves of the local newsagents;
especially as I'd been obsessed with creating my own magazines as a child. I
don't think my parents realised back then that one day I would be in print.</p>
<div class="post-image">
<img alt="Net 246" src="https://www.andismith.com/assets/blogs/2014/01/a-look-back-at-2013/net246cover.jpg">
</div>
<p>During <strong>November</strong> and December I've been working in Munich working on a
exciting AngularJS web application due to launch in the Spring.</p>
<p>For two years prior I'd led a development team prototyping ideas and designs, and this
launch will be the first part of the culmination of these efforts. To my
knowledge this is one of the most exciting and in depth uses of AngularJS yet.</p>
<div class="post-image">
<img alt="Munich" src="https://www.andismith.com/assets/blogs/2014/01/a-look-back-at-2013/munchen.jpg">
</div>
<p>2013 was full of exciting moments and varied challenges; and I'm looking
forward to further challenges in 2014. I already have a couple of articles and
talks lined up; and I've got a couple of open source projects that I'll be
revealing over the next month or two. If you have a challenge you think I'd be
interested in, <a href="https://www.linkedin.com/in/andismith/">please do get in touch</a>.
Happy New Year!</p>]]></content:encoded>
      <pubDate>Wed, 01 Jan 2014 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>jQuery On and Off Namespacing</title>
      <link>https://www.andismith.com/blogs/2013/02/jquery-on-and-off-namespacing</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2013/02/jquery-on-and-off-namespacing</guid>
      <description>Being able to apply and remove events with `on()` and `off()` is great, but sometimes there is a requirement to either trigger or remove a subset of events that have been added to an element. Here&apos;s how!</description>
      <content:encoded><![CDATA[<p>Quite a while ago, I wrote an article about <a href="http://www.andismith.com/blog/2011/11/on-and-off/">jQuery .on() and
off()</a> - an improved way of attaching events in jQuery - and still the best way to attach events to DOM elements in jQuery.</p>
<p>Being able to apply and remove events with <code>on()</code> and <code>off()</code> is great, but
sometimes there is a requirement to either trigger or remove a subset of
events that have been added to an element. For example:</p>
<pre><code class="language-javascript">$(".item").on("click", doThisCoolThing);
$(".item").on("click", doThisOtherCoolThing);

$(".item").trigger("click"); // both functions are triggered
$(".item").off("click"); // both functions are removed.
</code></pre>
<p>Using event namespacing we can assign names to event handlers when we create
them, and then use them later on to target specific functionality when we call <code>trigger()</code> or <code>off()</code>. The name you choose for your event namespace should be relevant for the functionality you are calling. For example:</p>
<pre><code class="language-javascript">$(".item").on("click.navigate", doThisCoolThing);
$(".item").on("click.notify", doThisOtherCoolThing);

// only the function assigned to the namespace navigate would be triggered.
$(".item").trigger("click.navigate");
// only the function assigned to the namespace notify would be removed.
$(".item").off("click.notify");
</code></pre>
<p>It's also possible to use multiple namespaces, like so:</p>
<pre><code class="language-javascript">$('.item').on('click.navigate.notify', doThisCoolThing);

// trigger the click event
$('.item').trigger('click.navigate');
function.
// remove the click event.
$('.item').off('click.notify');

</code></pre>
<p>Namespacing events in jQuery has been available for some time and isn't
limited to just <code>on()</code> and <code>off()</code>. It also works with <code>.bind()</code> and
<code>.unbind()</code> too.</p>]]></content:encoded>
      <pubDate>Tue, 05 Feb 2013 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Beginning Web Development</title>
      <link>https://www.andismith.com/blogs/2013/01/beginning-web-development</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2013/01/beginning-web-development</guid>
      <description>Inspired by a post I read earlier this week on how difficult it can be for newcomers to get in to the industry, I decided that I wanted to write a post with some hints and advice for developers who are just starting out their careers.</description>
      <content:encoded><![CDATA[<p>Inspired by a post I read earlier this week on how difficult it can be for
newcomers to get in to the industry, I decided that I wanted to write a post with
some hints and advice for developers who are just starting out their careers.</p>
<div class="update"><p><strong>Updated:</strong></p>
<ul>
  <li>7th March 2014 - Added some new links to other helpful resources.</li>
</ul>
</div>
<p>This post does not include any code or best practices and it is not a tutorial
or guide to writing web pages. Instead the points below are aimed toward
explaining what to expect as a web developer, and some of the key things you
should consider as early in to your journey as possible.</p>
<p>There are so many things to learn in web development now that it can feel
incredibly overwhelming - but it can also be very rewarding. What fascinated
me about web development was the ability to create - I love to create - and
when I started out building web pages I found it incredibly easy to express my
creativity with HTML and CSS in just a few lines of code. I could create
something and then publish it on the web for others to see very quickly (which
at the start of your career will likely just be your Dad and your best
friend).</p>
<ol>
<li><strong>There Are Multiple Ways To Do The Same Thing.</strong></li>
</ol>
<p>One of the hardest concepts to get your head around when you start working in
web development is that there can be be multiple ways to do exactly the same
thing.</p>
<p>For example, if you want to create a 2 column blog layout, there are a
multitude of ways to do this. For most things, there is no single answer -
there are multiple solutions to the same problem. Sometimes there will be
solutions that are better suited than others, but there are also solutions
that are equally valid as others; or solutions that are better in certain
scenarios.</p>
<blockquote>
<p>Use variation as a way to encourage your creativity.</p>
</blockquote>
<p>Try not to look at this as a negative thing, but instead use it as a way to
encourage your creativity. Make a decision on the solution to use this time
around and as you use it see what works and what doesn't work. You'll learn
more by trying a solution than forever searching the Internet for the perfect
answer, because the truth is...</p>
<ol start="2">
<li><strong>Your Code Will Never Be Perfect.</strong></li>
</ol>
<p>When you look back at the code you have written previously - whether it's one,
two or six months later, or even the next day or after the weekend - you will
see ways you can make things better. This is a good thing. It means you are
learning and improving.</p>
<blockquote>
<p>Don't be afraid to write<br>imperfect code.</p>
</blockquote>
<p>Don't be afraid to write imperfect code. With every site you develop, there will be a time that you have to let it go. It is better to be feature complete than to have 5 immaculate lines of code that do
10% of what you were trying to achieve. If someone wants to discuss with you
about what you have written, it's an opportunity to ask them how you can make
it better. It's a great way to learn; and you will find that different people
with different backgrounds will have different experiences they can share.</p>
<ol start="3">
<li><strong>You Will Never Know Everything About Web Development.</strong></li>
</ol>
<p>(But that doesn't mean you should stop trying)</p>
<p>Nobody can honestly say that they know everything about web development - not
every feature, every property and every browser bug. The scope of front end
web development is so broad that it covers three completely different
languages - HTML (markup), CSS (design and style) and JavaScript (logic).</p>
<p>Each has their own structure, concepts and quirks and each is continually
updated with new features. Unless you are building pages from a template, each
site you build is likely to require a different configuration and with that
come new challenges. There are also libraries, frameworks and APIs that you
will need to become accustomed with over time as the situation demands.</p>
<p>The truth is that you don't need to know everything straight away. You need to
know how to build a web page that meets your initial basic requirements. Then
going forward, you will need to know how to learn; who to talk to or where to
go to find out what you need to know to do your next task.</p>
<blockquote>
<p>Knowing where to look and how to pick things apart in a skill in itself.</p>
</blockquote>
<p>Knowing where to look and how to pick things apart in a skill in itself and one that is very
useful. You'll pick up knowledge and learnings as you go along.</p>
<ol start="4">
<li><strong>Google, MDN and Stack Overflow Are Your New Best Friends.</strong></li>
</ol>
<p>If you have a problem, there is a 99.99% chance that it has already been
solved. Multiple times. Using <a href="http://www.google.com/">Google</a> to search for a
problem can often return the right results if you include the language and
browser you are experiencing the issue in, e.g. "css ie6 margin".</p>
<p><a href="https://developer.mozilla.org/">MDN (the Mozilla Developer Network)</a> is an
excellent source for finding out more detail about HTML elements and
attributes; CSS styles and properties; and JavaScript APIs and methods. You can use
Google to search MDN by prefixing your search with "MDN", e.g. "mdn box sizing".</p>
<p><a href="http://www.stackoverflow.com">Stack Overflow</a> is a place where developers can
ask questions and receive answers from other members of the community. It is
always worth searching their answers database - particularly if you are unsure
of the exact term as the chances are someone with a similar amount of
knowledge has asked the question before. In fact so many
people have asked questions, I often find I skip the Google search and go
straight to <a href="http://www.stackoverflow.com">Stack Overflow</a> for an answer
nowadays.</p>
<p>If you've yet to start your web development journey, <a href="http://codeacademy.com/">Code
Academy</a> is as good a place to start as any.</p>
<p>If you're looking to improve your CSS, <a href="http://css-tricks.com/">CSS Tricks</a> is a
great site to bookmark.</p>
<ol start="5">
<li><strong>Not Everyone or Everything on the Web is Right.</strong></li>
</ol>
<p>(Especially me)</p>
<p>Just because someone has written a blog on a subject and that blog is the
number one result on Google, it doesn't make what your reading correct. The
author may just be really good at optimising their site for search engines
(known as Seach Engine Optimisation or SEO).</p>
<p>For instance, there are lots of articles and tutorials online but it can easy
to be mislead by one that is out of date. Check the date timestamp on the
tutorial you are reading to check if it is using current best practices. If
the tutorial doesn't have a date, then its worth cross referencing the
material against another article.</p>
<blockquote>
<p>Check the date timestamp on the tutorial you are reading.</p>
</blockquote>
<p>You can check comments on articles to see if others have left feedback saying
something doesn't work (people tend to be more vocal when something is broken)
before you go ahead and implement it.</p>
<p><a href="http://www.stackoverflow.com">Stack Overflow</a> and
<a href="https://developer.mozilla.org/">MDN</a> work well for getting the right answers.
On Stack Overflow, the first or second answer will usually be the answer that
you are looking for; and you can see how well received an answer is by the
community by looking at the number of votes it has next to it. On MDN, the
wiki nature of the documentation means that it can be kept up to date by
anybody rather than relying on one particular person to make updates - meaning
it is current most of the time.</p>
<p>Knowledge sites that are not community driven like W3Schools often contain
outdated information, so use with caution.</p>
<p>This also goes for libraries and APIs. Just because a library exists and has a
nice site doesn't mean the library is actually any good. Running a quick
Google search for problems with the library takes minutes and can save hours
of frustration.</p>
<ol start="6">
<li><strong>Things Will Always Change.</strong></li>
</ol>
<p>The web is constantly evolving. It has to. It needs to keep up with all the
new devices and the functionality that comes with those devices. Some browser
vendors now release new versions of their software every 6 weeks and with that
new release comes new features.</p>
<p>As an example of change, look at the devices people now use the browse the
web. Increasingly people browse on their mobile phones or tablet. Just 3 years
ago, the majority of people browsed the web on their computer. Not long before
that, there was no such thing as a tablet! Who knows what will happen next.<br>
Don't ever expect to stop learning. There will always be something new to
learn.</p>
<ol start="7">
<li><strong>Know Your Tools.</strong></li>
</ol>
<p>In any career, you can do a better job if you know the tools you are working
with. In web development as well as three separate languages, there are lots
of tools to learn.</p>
<p>Pick a text editor which highlights HTML, CSS and JavaScript syntax for you
and learn useful keyboard shortcuts so you have a smooth development
experience.</p>
<p>Choose a browser that will be your primary browser for development and get
accustomed to it's <a href="http://www.devtoolsecrets.com">Browser Developer Tools</a>. The first thing to find out how to do in the Browser Developer Tools is to see a list of the files that have
loaded on your page as an incorrect URL to something the page relies on can
stop styles, scripts or images from loading. A 404 error means the file could
not be found.</p>
<p>Finally, know the tools your user will visit your site in - download as many
browsers as your machine allows - there's Safari, Chrome, Opera, Firefox and
Internet Explorer. Get used to checking your work in each of these to learn
the quirks and differences between them. The developer tools in each of these
browsers include similar functionality, so if you find something useful to
debug in one browser, generally there will be a way to do it in the other
browsers' tools.</p>
<ol start="8">
<li><strong>Experiment.</strong></li>
</ol>
<p>When children are learning they often go through a phase of trying things that
they know are wrong in order to see what effect it has. They are
experimenting, and if you are developing for the web - so should you. I could
tell you to make sure you always close your brackets in CSS, but then you are
getting second hand information. Try it yourself, see what happens. If you
ever see the problem later on in your career (and you will) you will know
exactly what to do and where to start looking in order to fix it.</p>
<ol start="9">
<li><strong>Pick Apart Other People's Code.</strong></li>
</ol>
<p>One of the best things about the web is that it is open. You can view the
source of any page and see the HTML markup, CSS and JavaScript using the
Browser Developer Tools. Do you like the rounded corners used on your mates'
blog? Right click on it and select "Inspect Element" in most browsers* to see
how they did it.</p>
<p>Open projects such as the <a href="http://html5boilerplate.com/">HTML5 Boilerplate</a>
and read the comments to analyse why certain parts of code are included.
Look at other people's projects on <a href="http://codepen.io/">CodePen</a> to see how certain
visual effects and techniques were produced.</p>
<p>* In Safari, you'll need to enable the developer toolbar. Go to Safari in the Menu Bar, then
"Preferences" > "Advanced" > "Show Develop Menu in Menu Bar".</p>
<ol start="10">
<li><strong>Start a Blog.</strong></li>
</ol>
<p>The best way to remember what you have learnt is to keep a log of your
findings. Writing down what you have learnt will reinforce it in your head and
may encourage you to think of "what if" situations.</p>
<blockquote>
<p>Writing down what you have learnt will reinforce what you've learnt.</p>
</blockquote>
<p>Since we live in a digital age, why not start a blog? This way, people are invited to read what you have
learnt and contribute advice to help make you a better developer; and you can
use your blog as a test bed for trying out your new coding skills!</p>
<p>For the web developers who are beginning their career, I hope that you have
found this list useful and wish you the best of luck with your future
webpages! For me, becoming a web developer was the best career choice I ever
made.</p>
<p>I'm always looking for talking opportunities so if any schools, colleges or
universities (particularly in the UK) would like me to come give a talk on
this, please contact me.</p>
<p>If you are a web developer and have any further points to add to this list,
feel free to leave a comment! If this post proves popular, I will likely write
a follow up that focuses on tips for each of the languages.</p>]]></content:encoded>
      <pubDate>Mon, 07 Jan 2013 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Media Queries: The Next Generation</title>
      <link>https://www.andismith.com/blogs/2013/01/media-queries-next-gen</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2013/01/media-queries-next-gen</guid>
      <description>2012 really felt like a progressive step forward for Responsive Web Design. Using a responsive grid became popular as a convienent way of handling column configurations over breakpoints.</description>
      <content:encoded><![CDATA[<p>2012 really felt like a progressive step forward for Responsive Web Design. As
big corporate companies like <a href="http://www.starbucks.com/">Starbucks</a> and
<a href="http://www.microsoft.com">Microsoft</a> as well as
<a href="https://www.gov.uk/">governments</a> began to introduce breakpoints in to their
web pages to cater for the diverse range of devices now available on the
market, it no longer felt like the technology was only for use for blogs and
news sites. Responsive Design proved it was ready for mass consumption.</p>
<p>In parallel, using a responsive grid became popular as a convienent way of
handling column configurations over breakpoints. From simple grids like the
one used in <a href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a> to the
SASS driven <a href="http://susy.oddbird.net/">SUSY</a> that allows you to keep semantic
markup while managing columns over breakpoints; grids in a responsive design
became popular.</p>
<p>Using these techniques, we could ensure that content was king and provide the
optimal experience for our users regardless of how they were viewing our web
page.</p>
<h2>There Appears To Be a Problem.</h2>
<p>But for all the possibilities media queries allow, there appears to be a
problem with the current selection of available conditions. We're often told
that we should <a href="http://www.lukew.com/ff/entry.asp?1598">build for content
first</a> and not design, yet the most
widely used media queries are built in completely the opposite way: <code>min- width</code> and <code>max-width</code>. These two media queries measure the width of the page</p>
<ul>
<li>often this is useful but there are situations where this isn't helpful,
particularly on larger scale websites.</li>
</ul>
<p>Suppose we were to create a small module on our page, for example a carousel,
which sits across two columns of our three column grid. This module will be
used across multiple pages and we are unsure how it will be configured over
these pages.</p>
<div class="post-image">
<img alt="2 Column" src="https://www.andismith.com/assets/blogs/2013/01/media-queries-next-gen/2-column.jpg">
</div>
<p>In a responsive design, we may create two or more breakpoints for this module
to ensure that items resize as the page gets smaller, such as reducing the
size of the font. Our module is responsive. But the media queries that
determine when these breakpoints occur are based on the page width not the
container width, so this module is actually only responsive within the context
of placing it within two columns.</p>
<p><a href="http://www.andismith.com/examples/media-queries-problem/">You can view a demo of the responsive two column carousel
here</a> (note the
carousel is static for the purposes of this demo).</p>
<p>If we use the same module on another page, but give it a different number of
columns, then the breakpoints we created before may be rendered impractical
(or worse). Let's look at the same carousel module but sitting across three
columns:</p>
<div class="post-image">
<img alt="3 Column" src="https://www.andismith.com/assets/blogs/2013/01/media-queries-next-gen/3-column.jpg">
</div>
<p>Because we've changed the size of the container, the breakpoints set for this
search module are now sub-optimal for the configuration of columns. The things
we reduced in size before now shrink too early, and we are left with a lot of
extra space.</p>
<p>[You can view a demo of the responsive three column carousel with our original
breakpoints here](http://www.andismith.com/examples/media-queries-
problem/index-3-col.html).</p>
<p>This is not such a big problem for three columns, it's a bit small but we can
usually get away with this. Let's see what happens when we reduce the number
of columns our module spans.</p>
<p>If we change our module container to span a single column the problem is much
worse. Now the module container is too small, so the media queries we provided
to shrink our content are inappropriate. Our page looks a mess.</p>
<div class="post-image">
<img alt="1 Column" src="https://www.andismith.com/assets/blogs/2013/01/media-queries-next-gen/1-column.jpg">
</div>
<p>[You can view a demo of the responsive one column carousel with our original
breakpoints here](http://www.andismith.com/examples/media-queries-
problem/index-1-col.html).</p>
<h2>A Solution</h2>
<p>There's no point complaining about a problem without proposing a solution; and
the solution in this case seems to be the argument for a new type of media
query which can react to a container's properties rather than the page
properties. In the case of our example, this would be a media query that can
react based on the width of a container defined by the user rather than the
whole width of the page.</p>
<p>In an ideal world, perhaps we'd solve this problem in CSS like this:</p>
<pre><code class="language-css">
.container {

  @media screen and (max-width: 400px) {
    h3 {
      /* narrow styles */
    }
  }

  @media screen and (min-width: 401px) and (max-width: 800px) {
    h3 {
      /* wider styles */
    }
  }

}
</code></pre>
<p>But CSS doesn't support nested queries like SASS and LESS does, so perhaps a
more suitable solution to the problem would be to create new media queries
conditions <code>min-width-of</code> and <code>max-width-of</code> (and the equivalent height
conditions) like so:</p>
<pre><code class="language-css">
@media screen and (max-width-of(.container): 400px) {

  .container h3 {
    /* narrow styles */
  }
}

@media screen and (min-width-of(.container): 401px) and (max-width-of(.container): 800px) {

  .container h3 {
    /* wider styles */
  }

}
</code></pre>
<p>Now when we resize the page, our content would be able to resize when the
container meets the condition, rather the page.</p>
<h2>Conclusion</h2>
<p>For rich content web sites, responsive web design can be a complicated
procedure; and as developers push responsive in to 2013 the problem is only
going to get more complicated.</p>
<p>Grids can help reduce complexity, but we are still reliant on measuring page
properties rather than the properties of the actual container that holds our
content. Here's hoping that in the future it becomes possible to create media
queries based on containers so that we can allow greater flexibility on our
web pages.</p>
<h2>Appendix: Until Then</h2>
<p>Not wanting to detract from the article above, I did want to share a solution
that is possible in today's browsers, although it isn't particularly 'nice'
and relies on JavaScript.</p>
<p>By using JavaScript to calculate and compare the container and page widths, we
could apply a class to our container to determine how many columns it is
currently occupying. This class would then drive the size of our content in
the same way our media queries were in the above examples. Like I said, not a
pretty solution - but a workaround until the bigger issue is resolved.</p>]]></content:encoded>
      <pubDate>Thu, 03 Jan 2013 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Sublime Text 2 Cheatsheets</title>
      <link>https://www.andismith.com/blogs/2012/12/sublime-text-2-cheatsheets</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/12/sublime-text-2-cheatsheets</guid>
      <description>As my Christmas present to you, here are 2 cheatsheets (one for Mac, one for Windows) so you can learn and reference those shortcuts to improve yourself and your developer skills.</description>
      <content:encoded><![CDATA[<p>They say that a workman is only as good as his tools, and the same is true for
a web developer.</p>
<p>The more you know about the tools, the better you'll get at
performing the task at hand. There are now many tools that can speed up web
development - from <a href="http://www.andismith.com/blog/2011/11/25-dev-tool-secrets/">Browser Developer
Tools</a> to build
scripts like <a href="http://gruntjs.com/">Grunt</a>; but the most important tool of all
is the tool you write your code in - your code editor. For me, that's Sublime
Text 2 and there are a ton of keyboard shortcuts available to make life easier
available.</p>
<p>So as my Christmas present to you, here are 2 cheatsheets (one for Mac, one
for Windows) so you can learn and reference those shortcuts to improve
yourself and your developer skills.</p>
<p>PC (click thumbnail to open):</p>
<div class="post-image">
<a href="http://andi.so/sl2pc"><img alt="Sublime Cheatsheet PC" src="https://www.andismith.com/assets/blogs/2012/12/sublime-text-2-cheatsheets/sublime-windows.png"></a>
</div>
<p>MAC (click thumbnail to open):</p>
<div class="post-image">
<a href="http://andi.so/sl2mac"><img alt="Sublime Cheatsheet Mac" src="https://www.andismith.com/assets/blogs/2012/12/sublime-text-2-cheatsheets/sublime-mac.png"></a>
</div>
<p>Have a great Christmas everyone!</p>]]></content:encoded>
      <pubDate>Mon, 24 Dec 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Canvas Animated Particles 3D Effect in 5 Minutes</title>
      <link>https://www.andismith.com/blogs/2012/12/canvas-animated-particles-3d</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/12/canvas-animated-particles-3d</guid>
      <description>A couple of months ago, I gave a PechaKucha talk about creating a 3D animated particles effect with the canvas element.</description>
      <content:encoded><![CDATA[<p>A couple of months ago, I gave a
<a href="http://en.wikipedia.org/wiki/PechaKucha">PechaKucha</a> talk about creating a 3D
animated particles effect with the canvas element.</p>
<p>For those who've not come across the concept of a PechaKucha before, the idea
is to present on a topic for 20 slides with 20 seconds per slide. The event I
attended was slightly different with just 5 minutes to show those 20 slides
(so 15 seconds a slide).</p>
<p>Whenever I talk to web developers I find that the canvas element still remains
a mystery to most, so I wanted to do a short presentation that could introduce
developers who had not used the canvas element before to the concepts and show
how it's not quite as scary as it seems.</p>
<p>It was my first PechaKucha, and during rehearsal I learnt that the format
doesn't lend itself too well to explaining how to implement lines and lines of
code. I tried to balance out the slides so there wasn't too much new
information on each. The aim of the talk was less about teaching developers
everything about canvas and more about showing how simple it can be to create
an effect.</p>
<p>Below is a write up of the talk, and I'm going to purposefully keep it short
(hopefully a 5 minute read) to allow you time to experiment. <a href="http://andi.so/p-3d5m">You can view the
original presentation here</a>, the slides move on every
15 seconds and the animated demos start themselves - each demo is deliberately
self contained so you can dive in to the code. Please use, abuse and learn.</p>
<h2>The 5 Minute 3D Effect</h2>
<p>Quick note: this is a 3D effect in 2D, not a WebGL demo. The effect we are
going for is a simple space effect with particles flying out of the center of
the canvas element, it looks like the following:</p>
<div class="post-image">
<img alt="3D Particles Effect" src="https://www.andismith.com/assets/blogs/2012/12/canvas-animated-particles-3d/particles.jpg">
</div>
<h3>The Canvas element</h3>
<ul>
<li>Allows us to place a 2D drawing canvas on our page</li>
<li>Interfaced with JavaScript</li>
<li>Default dimensions: 300 x 150</li>
<li>Works in IE9+</li>
</ul>
<pre><code class="language-markup">&#x3C;canvas id="particles">
  Stop running IE6-8 dude, it's not cool!
&#x3C;/canvas>
</code></pre>
<h3>The Canvas Context</h3>
<p>The context is like an artists toolbox. We use it to:</p>
<ul>
<li>Draw lines, rectangles and arcs</li>
<li>Set colours and styles</li>
<li>Fill shapes</li>
<li>Clear the canvas</li>
<li>Get/use image data</li>
</ul>
<pre><code class="language-javascript">var canvas = document.getElementById('particles');
var context = canvas.getContext('2d');</code></pre>
<h3>Set up our Drawing Area</h3>
<p>We will create a function called <code>draw()</code> that will be called for each frame
of our animation. This function will need to carry out a number of tasks:</p>
<ul>
<li>Clear the canvas - as our canvas is a bitmap we cannot move items on it, so for each frame we have to redraw the canvas.</li>
<li>Translate our canvas so our point of origin (0, 0) is the center, not the top left.</li>
<li>We use <code>save()</code> and <code>restore()</code> to save/restore the state of the context (e.g. point of origin, colors). It does not save an image of the canvas.</li>
</ul>
<pre><code class="language-javascript">function draw() {
  context.clearRect(0, 0, width, height);

  context.save();
  context.translate((width/2), (height/2));

  // draw particles

  context.restore();
}</code></pre>
<h3>Decide On a Amount of Particles</h3>
<p>We need to decide how many particles we wish to show. Naturally in a
presentation, the audience will always choose the highest number you offer
(10,000) - but in real life consider number of particles vs. performance. Our
particles will live in an array in JavaScript.</p>
<pre><code class="language-javascript">var AMOUNT = 10000;
var particles = [];</code></pre>
<h3>Create Those Particles!</h3>
<p>Each particle is an object and for each particle we:</p>
<ul>
<li>Make an object with x, y, z co-ordinates</li>
<li>Push it on to our particles array</li>
</ul>
<pre><code class="language-javascript">function create() {
  var i = AMOUNT;
  var particle;

  while (i--) {
    particle = {
      x: 0,
      y: 0,
      z: 0
    };
    particles.push(particle);
  }
}</code></pre>
<h3>Generate Random Numbers</h3>
<p>At the moment all of our particles are sitting at the same co-ordinates (the
center), so we need to give them some random coordinates, which means we need
a simple function to generate random numbers. This function:</p>
<ul>
<li>Gets the difference between max and min</li>
<li>Multiplies the difference by <code>Math.random()</code></li>
<li>Adds min back to our value to ensure it's within our range</li>
</ul>
<pre><code class="language-javascript">function randomNumber(min, max) {
    return (Math.random() * (max - min)) + min;
}</code></pre>
<h3>Create Random Particles!</h3>
<p>Now we've done that, we adjust our particle creation loop for each particle
to:</p>
<ul>
<li>Set x to a random number based on half the width (remember our origin is the center)</li>
<li>Set y to a random number based on half the height</li>
<li>Set z to 0 (for the moment)</li>
</ul>
<pre><code class="language-javascript">particle = {
  x: randomNumber(-(width/2), (width/2)),
  y: randomNumber(-(height/2), (height/2)),
  z: 0
};</code></pre>
<h3>Draw Our Particles!</h3>
<p>Now we have differing co-ordinates, we loop through our particles array and
make each particle a circle using <code>context.arc(x, y, radius, startAngle, endAngle, antiClockwise)</code>.</p>
<pre><code class="language-javascript">context.fillStyle='rgba(255,255,255,0.3)';
context.beginPath();
context.arc(particle.x, particle.y, radius, 0, Math.PI*2, true);
context.fill();</code></pre>
<p>Here, we are choosing to color our particles white, during our arc and then
filling it. I've added a nice semi-transparent effect to the color by
specifying an alpha in our rgba color value. You can improve performance by
only setting the colour once rather than for each particle.</p>
<h3>Field of Vision</h3>
<p>The z coordinate measures how close or far away our particle is from our view.
If it gets too close, it will obscure our view so we want to simulate it going
past us at some point. If it gets too far away, we are spending time rendering
a particle that is practically invisible. So we use this value to tell our
draw function when to draw the particle. It's up to you which value you use,
~250 seems to work well.</p>
<pre><code class="language-javascript">var FIELDOFVISION = 250;
particle = {
  x: randomNumber(-(width/2),(width/2)),
  y: randomNumber(-(height/2),(height/2)),
  z: randomNumber(-FIELDOFVISION,FIELDOFVISION)
};</code></pre>
<h3>Scaling Our Particles</h3>
<p>Particles should be different sizes at different points on the z axis, so we
use scale to position them and to change the radius of our circles.</p>
<pre><code class="language-javascript">scale = (FIELDOFVISION/(particle.z+FIELDOFVISION));
x = (particle.x * scale);
y = (particle.y * scale);
context.arc(x, y, scale, 0, Math.PI*2, true);</code></pre>
<h3>Animation</h3>
<p>Our particles are now pretty, scaled and scattered, but still static. We use
<code>requestAnimationFrame</code> to draw when the browser reflows, for the best
performance.</p>
<p>Add a quick shim:</p>
<pre><code class="language-javascript">window.requestAnimationFrame ||
  (window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function( callback ){
    window.setTimeout(callback, 1000 / 60);
  });</code></pre>
<p>Then we call <code>draw()</code> like so:</p>
<pre><code class="language-javascript">requestAnimationFrame(draw);</code></pre>
<h3>Make Our Particles Move!</h3>
<p>To make the particles move, for each frame we need to change the z co-
ordinate.</p>
<pre><code class="language-javascript">SPEED = -4;</code></pre>
<p>Within our draw loop:</p>
<pre><code class="language-javascript">particle.z += SPEED;</code></pre>
<p>We also want to test for the field of vision and move our particles to the
front or back when they are no longer in vision. This has the bonus of
reducing the amount of array manipulation (we're just editing values rather
than splicing and splitting arrays) improving performance.</p>
<pre><code class="language-javascript">if (particle.z &#x3C; -FIELDOFVISION) {
  particle.z = FIELDOFVISION;
}</code></pre>
<h3>Done!</h3>
<p>And that's it, we have particles that move! Simple, and nothing really to be
scared of.</p>
<p><a href="http://andi.so/p-3d5m">Check out my presentation for demos; a few more effects and a nice picture of
a cat!</a> Enjoy!</p>]]></content:encoded>
      <pubDate>Sun, 23 Dec 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Extending getUserMedia With Canvas</title>
      <link>https://www.andismith.com/blogs/2012/07/extending-getusermedia</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/07/extending-getusermedia</guid>
      <description>Following on from my previous post which introduced us to getUserMedia, I wanted to share two ways you can extend getUserMedia&apos;s video capture using my good friend, the canvas element.</description>
      <content:encoded><![CDATA[<p>Following on from [my previous post which introduced us to
getUserMedia](http://www.andismith.com/blogs/2012/06/introduction-to-
getusermedia/), I wanted to share two ways you can extend getUserMedia's video
capture using my good friend, the canvas element.</p>
<p>What we'll cover in this post:</p>
<ul>
<li>Photo Booth - Using canvas to take screenshots.</li>
<li>Effects Studio - Using canvas to apply different effects to the webcam feed.</li>
</ul>
<p>Now we know what we're doing and we've given them funky names, let's begin!</p>
<h2>Extending with Canvas</h2>
<p>If you've not come across the canvas element before, it is used to render a
bitmap image to the user's screen via JavaScript. Unlike SVG, it has no DOM;
and it also has no history of previous renders.</p>
<p>Our first step is to add a canvas element to our page.</p>
<pre><code class="language-html">&#x3C;canvas id="photo">&#x3C;/canvas>
</code></pre>
<h3>Photo Booth</h3>
<p>To take screenshots, we will need to add a button to our page; and attach an
event.</p>
<p>The following function creates a button programatically and attaches an event
to call the function <code>takePhoto()</code> (which we've yet to write) when the user
clicks the button.</p>
<pre><code class="language-javascript">function setupPhotoBooth() {
  var photoButton = document.createElement("button");
  photoButton.innerText = "Smile!";
  photoButton.addEventListener("click", takePhoto, true);
  document.body.appendChild(photoButton);
}
</code></pre>
<h4>Taking our Photo</h4>
<p>Taking the photo isn't too difficult. First, we need to set our photo canvas element to have the same width and height as our video otherwise our picture will be squished into canvas' 300 x 150 default dimensions. If you are having difficulty getting video's height and width, you may need to explicitly size the video element.</p>
<p>Next, we need to get the 2D context of the photo canvas. If you're unfamiliar with how canvas works, the context is like an artist - it will get and set our image data.</p>
<p>Finally, we call <code>drawImage</code> with the parameters of our video element, and the area we wish to paint in.</p>
<pre><code class="language-javascript">function takePhoto() {
  var video = document.querySelector("video");
  var photo = document.getElementById("photo");
  var context = photo.getContext("2d");

  // set our canvas to the same size as our video
  photo.width = video.width;
  photo.height = video.height;

  context.drawImage(video, 0, 0, photo.width, photo.height);
}
</code></pre>
<p>Running the page in a browser that supports getUserMedia, you should now be
able to take a photo. Unlike me, you may want to sort out your hair before
hitting the "Smile!" button.</p>
<div class="post-image">
<img alt="GUM Photo" src="gum-photo-small.jpg" srcset="gum-photo-small.jpg 640w, gum-photo-medium.jpg 1024w, gum-photo-large.jpg">
</div>
<p>The view on the left is our feed, the one on the right is our picture.</p>
<h4>Saving our Image</h4>
<p>If you would like to extend your photo booth to save the image being taken,
you can add a save button to your page and hook up an event to save the photo.
To trigger a download, we need to change the MIME type to image/octet-stream
like below.</p>
<pre><code class="language-javascript">function savePhoto() {
  var data = photo.toDataURL("image/png");
  data = data.replace("image/png", "image/octet-stream");
  document.location.href = data;
}
</code></pre>
<p>This will save the image as a PNG. If you wish to save as other image formats, take a look at <a href="http://www.nihilogic.dk/labs/canvas2image/">Jacob Siedelin's Canvas2Image
library</a>.</p>
<p>If you'd like to look at and play with the photo booth web cam demo, [I've
uploaded a demo to Github](http://andismith.github.com/getUserMedia-
examples/). Don't forget, you'll need [a browser that supports
getUserMedia](http://www.andismith.com/blog/2012/06/introduction-to-
getusermedia/) to use your webcam.</p>
<p>[You can view and fork the code on Github.](https://github.com/andismith
/getUserMedia-examples)</p>
<h3>Effects Studio</h3>
<p>One of the first things I ever remember using on the Mac was PhotoBooth, where you can adjust your image to make a variety of different effects - such as changing the colours or saturation. Let's have some fun with our webcam feed.</p>
<p>We could use CSS filters to <a href="http://html5-demos.appspot.com/static/css/filters/index.html">add effects to our images</a>.. this would be a perfectly valid way of adding effects, except if we saved the image, our effect would not be applied. So let's take the more complicated route.</p>
<p>As we want to manipulate our live feed and we cannot change the video feed
directly, the first thing we need to do is change our video feed to show in a canvas element. We've already covered how to grab a video image and draw it in the canvas, but this time we are going to need to continually refresh the canvas element to keep a live feed.</p>
<p>Previously in JavaScript, timed events could be achieved with <code>setInterval</code> or <code>setTimeout</code>; but more recently <code>requestAnimationFrame</code> has been introduced.
<code>requestAnimationFrame</code> allows the browser to synchronise our animation with the browser paint cycle and won't run the animation while the tab is not active. Sadly, it's another case of vendor prefixing for <code>requestAnimationFrame</code> so we need another shim (<a href="http://paulirish.com/2011/requestanimationframe-for-smart-animating/">Paul Irish goes in to more detail about this shim on his blog</a>).</p>
<pre><code class="language-javascript">window.requestAnimationFrame ||
  (window.requestAnimationFrame =
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function (callback) {
      window.setTimeout(callback, 1000 / 60);
    });
</code></pre>
<p>requestAnimationFrame works in the same way as setTimeout - it is only called once and we are required to call it each time we want to use it. We fallback to setTimeout for browsers that don't support it (which shouldn't be any of the browsers that support getUserMedia, but it's best to be safe.</p>
<p>Now, we need to add the additional canvas element to our page:</p>
<pre><code class="language-html">&#x3C;canvas id="feed">&#x3C;/canvas>
</code></pre>
<p>We need to create a new function to stream the feed. Using the same method we
used to take a photo, we draw an image to the feed context. This time round we
also make the function call itself via requestAnimationFrame.</p>
<pre><code class="language-javascript">function streamFeed() {
  var context = feed.getContext("2d");
  requestAnimationFrame(streamFeed);
  context.drawImage(video, 0, 0, feed.width, feed.height);
}
</code></pre>
<p>The final thing to do to get our stream working is to add a call to initiate <code>streamFeed()</code> within our getUserMedia success function (called <code>onSuccess</code> in our previous post).</p>
<p>Although our stream will now work, we don't want both the video and canvas
feed to appear at the same time, so we need to hide our video.</p>
<pre><code class="language-javascript">video.style.display = "none";
</code></pre>
<h4>Separating the Raw Feed from Display</h4>
<p>We've finished doing the setup work, now it's time to add some effects. To add effects, we are going to want to add another canvas element called display.
Our feed will provide the image data, and our display will show the finished result. Let's add the element, hide our feed, and also give our feed and display canvas elements a width and height to avoid the default canvas dimensions when we are manipulating data.</p>
<pre><code class="language-javascript">feed.style.display = "none";
feed.width = 640;
feed.height = 480;

display.width = 640;
display.height = 480;
</code></pre>
<h4>Effects Groundwork</h4>
<p>We'll store our applied effects in an options array, but we need a way to
allow the user to trigger our effects. The following function creates an
effects button for each of our effects. The effects we are going to add are:
"invert", "red", "blue" and "green".</p>
<pre><code class="language-javascript">function setupEffectsButtons() {
  var effects = ["invert", "red", "blue", "green"];
  var effectButton;

  for (var i = 0, l = effects.length; i &#x3C; l; i++) {
    effectButton = document.createElement("button");
    effectButton.id = effects[i];
    effectButton.innerText = effects[i];
    effectButton.addEventListener("click", toggleEffect, true);
    document.body.appendChild(effectButton);
  }
}
</code></pre>
<p>These buttons trigger the <code>toggleEffect</code> function which adds and removes items from our options array when the user clicks the button. The actual effects will be added during our animation loop, which we will cover afterwards.</p>
<pre><code class="language-javascript">function toggleEffect(e) {
  var effect = this.id;
  if (options.indexOf(effect) > -1) {
    options.splice(options.indexOf(effect), 1);
  } else {
    options.push(effect);
  }
}
</code></pre>
<p>Now we have a way for the user to add and remove effects, it's time to
manipulate our feed image data!</p>
<h4>Changing the Stream Feed</h4>
<p>We need to alter our stream feed function to include a call to the function
that will add our effects (<code>addEffects()</code>).</p>
<p>First we need to draw our video to the feed canvas so we can extract the image
data. Next, we call <code>getImageData</code> to extract the image data. This is what we
will pass through to <code>addEffects()</code>. Finally, we will output the returned
image data on to our display context.</p>
<pre><code class="language-javascript">function streamFeed() {
  var feedContext = feed.getContext("2d");
  var displayContext = display.getContext("2d");
  var imageData;

  requestAnimationFrame(streamFeed);
  feedContext.drawImage(video, 0, 0, display.width, display.height);
  imageData = feedContext.getImageData(0, 0, display.width, display.height);
  imageData = addEffects(imageData);
  displayContext.putImageData(imageData, 0, 0);
}
</code></pre>
<h4>Adding Effects</h4>
<p>Our <code>addEffects()</code> function loops through our options and applies each of them
to our image data. First, let's write the skeleton.</p>
<pre><code class="language-javascript">function addEffects(imageData) {
  var data = imageData.data;
  var type = {};

  for (var i = 0; i &#x3C; options.length; i++) {
    type = options[i];

    for (var j = 0; j &#x3C; data.length; j += 4) {
      switch (type) {
        case "invert":
          // code to go here
          break;

        case "red":
          // code to go here
          break;

        case "blue":
          // code to go here
          break;

        case "green":
          // code to go here
          break;

        default:
          break;
      }
    }
  }
  return imageData;
}
</code></pre>
<p>For each option, we will need to loop through the image data. Each pixel in
our image data is represented by four numeric values - the first is red (from
0 to 255), the second blue, the third green and the fourth is alpha - known as
RGBA. Therefore our loop will increment by 4 each time to reach the next
pixel.</p>
<p>After checking our page still works, it's time to add our effects. The effects
for red, blue and green are created in a similar fashion. For each effect, we
increase the corresponding RGB value and decrease the other colour values.
Using <code>Math.min</code>, we can choose whether it should be twice the value from the
feed data, or 255 - whichever is lower. The following code is for red. The
effects for green and blue are the same formula, but on the other related
values in the array:</p>
<pre><code class="language-javascript">case "red":
  data[j] = Math.min(255,data[j] * 2);
  data[j + 1] = data[j + 1] / 2;
  data[j + 2] = data[j + 2] / 2;
  break;
</code></pre>
<p>Finally, let's add our code for invert. Invert is a straightforward effect to
create - we just invert the pixel value by subtracting the value from 255,
like so:</p>
<pre><code class="language-javascript">case "invert":
  data[j] = 255 - data[j];
  data[j + 1] = 255 - data[j + 1];
  data[j + 2] = 255 - data[j + 2];
  break;
</code></pre>
<h4>And... Done!</h4>
<p>Now we've created our effects, we can now apply effects to our webcam feed.
Multiple effects can be applied by pressing multiple buttons. If we wish to
apply our effects to our photo snaps, we need to change our photo booth
<code>takePhoto()</code> code to look at the display canvas rather than the feed canvas.</p>
<div class="post-image">
<img alt="GUM Effects" src="gum-effects-small.jpg" srcset="gum-effects-small.jpg 640w, gum-effects-medium.jpg 1024w, gum-effects-large.jpg">
</div>
<p>If you'd like to look at and play with the effects web cam demo, [I've
uploaded a demo to Github](http://andismith.github.com/getUserMedia-
examples/). Don't forget, you'll need [a browser that supports
getUserMedia](http://www.andismith.com/blog/2012/06/introduction-to-
getusermedia/) to use your webcam.</p>
<p>[You can view and fork the code on Github.](https://github.com/andismith
/getUserMedia-examples)</p>
<p>If you create effects code for other different effects, feel free to share it below!</p>]]></content:encoded>
      <pubDate>Sun, 01 Jul 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Introduction to getUserMedia</title>
      <link>https://www.andismith.com/blogs/2012/06/introduction-to-getusermedia</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/06/introduction-to-getusermedia</guid>
      <description>For a long time in the &apos;Flash vs HTML5&apos; comparison arguments, one advantage Flash had was the ability to capture audio and video from the users computer. With upcoming browser releases, it is now possible to do this with JavaScript and the HTML5 video element, and its very simple to do.</description>
      <content:encoded><![CDATA[<p>It's been a while since my last blog post, but I've actually been working on a number of exciting things in the last couple of months, so I'm going to take this opportunity to write about one of them - video/audio capture.</p>
<p>For a long time in the "Flash vs HTML5″ comparison arguments, one advantage Flash had was the ability to capture audio and video from the users computer.
With upcoming browser releases, it is now possible to do this with JavaScript and the HTML5 video element, and its very simple to do. Unfortunately, audio doesn't work through the <code>&#x3C;audio></code> element at the time of writing, but video does - so let's use that!</p>
<h2>Implementation</h2>
<p>Firstly you'll need a browser that supports getUserMedia. Currently this list is Chrome Canary 18+ (turned on from about:flags) and Opera 12+ - although it will be [Firefox nightly](http://mozillamediagoddess.org/2012/06/25/the-first-
part-of-webrtc-has-landed/) any minute now. You'll also need a machine that has a webcam and microphone, which should be any laptop nowadays.</p>
<p>On your HTML page, you need to include an empty <code>&#x3C;video></code> element. This will be used to show the feed from the video stream.</p>
<p>Due to the joy of vendor prefixes, we need to make sure we're referencing getUserMedia correctly. This quick shim helps us out:</p>
<pre><code class="language-javascript">navigator.getUserMedia || (navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia);</code></pre>
<p>Note, I haven't left out the Opera prefix because I hate Opera (I don't) - but
instead because Opera have implemented getUserMedia unprefixed.</p>
<h3>Checking for Browser Support</h3>
<p>The next step is to check the browser we're using has an implementation of
getUserMedia and if it does, request a video and audio stream.</p>
<pre><code class="language-javascript">if (navigator.getUserMedia) {
  navigator.getUserMedia({video: true, audio: true, toString : function() {
      return "video,audio";
    }
  }, onSuccess, onError);
} else {
    // getUserMedia is not supported in this browser.
}</code></pre>
<p>We pass an the object with video and audio set to true (<code>{ video: true, audio: true }</code>) to tell the browser we wish to capture both video and audio, but we
could set either of these to false to only capture one (when audio works, that
is). Unfortunately before Chrome Canary 20, Chrome required a string for this
parameter so the included toString function hack is actually a way around this
(credit to <a href="http://www.twitter.com/agektmr">@agektmr</a>).</p>
<p>The next two parameters are the function to call when getUserMedia is
successful, and what to call when there is an error (such as the user declines
the invitation to capture video and audio). So, our next step is to write
these functions!</p>
<h3>Handling Success</h3>
<pre><code class="language-javascript">function onSuccess(stream) {

  var source;

  if (window.webkitURL) {
    source = window.webkitURL.createObjectURL(stream);
  } else {
    source = stream; // Opera and Firefox
  }

  var video = document.querySelector('video');
  video.autoplay = true;
  video.src = source;

}</code></pre>
<p>A successful connection provides us with a stream object. Different browsers
have different implementations of this - Opera and Firefox set this directly
to the stream object, while WebKit provides a Blob URL.</p>
<p>We then need to tell our <code>&#x3C;video></code> element to autoplay; set the source and we
have written our success function.</p>
<h3>Handling Failure</h3>
<p>An error may occur in our request such as if the user denies us permission to
capture their video, so we need to create an error function that alerts the
user to this (you could print something out to the screen, or use an alert).
Once that's done, we're good to go!</p>
<h3>See Yourself!</h3>
<p>Load your page in your favourite browser - you'll need to set it up to run on
a HTTP server as permissions won't be requested from the file protocol - and
your page should ask for you to share your webcam and microphone. Hit accept,
and you should hopefully see a face prettier than mine.</p>
<div class="post-image">
<img alt="Not very pretty photo of Andi" src="gum-andi-small.jpg" srcset="gum-andi-small.jpg 640w, gum-andi-medium.jpg 1024w, gum-andi-large.jpg">
</div>
<p>I've created a [really simple demo](http://www.andismith.com/examples/blog
/getusermedia-simple/) if you'd like to play and poke around with the code.</p>]]></content:encoded>
      <pubDate>Sat, 30 Jun 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>CSS3 Flexbox Carousel</title>
      <link>https://www.andismith.com/blogs/2012/05/css3-flexbox-carousel</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/05/css3-flexbox-carousel</guid>
      <description>Carousels are one of the most common components web developer&apos;s build. As other trends come and go, carousels tend to stay.</description>
      <content:encoded><![CDATA[<p>Carousels are one of the most common components web developer's build. As other trends come and go, carousels tend to stay.</p>
<p>Recently I've been using the <code>nth-child</code> CSS pseudo selector when building CSS3 demos for my "<a href="http://blog.crazyegg.com/?s=andismith&#x26;x=0&#x26;y=0">Introduction to CSS3″ series on The Daily
Egg</a>; to the point where <code>nth-child</code> became a bit of a habit. For those of you who have not come across <code>nth-child</code>, it allows you to target a particular child element by number or a simple formula.</p>
<p>So I was building a carousel the other week and I used <code>nth-child</code> to create my dummy carousel items. Each item had a different background color.</p>
<p>It was all working splendidly until I needed to make the carousel loop. Then as it looped, my <code>nth-child</code> changed. As you are probably aware, the problem with looping carousels is that you have to repeat your items so something can animate in from the left or right.</p>
<img alt="Looping carousel" src="https://www.andismith.com/assets/blogs/2012/05/css3-flexbox-carousel/looping.gif">
<p>Repeating items in a carousel has always bothered me. It feels like a messysolution, but it gives the right visual appearance and works cross browser.</p>
<p>Then I began thinking about a presentation I gave recently on "Layouts of the Future", in which I spoke about current hacks we have to make to create CSS layouts and how help is round the corner with the likes of columns; CSS Regions and… flexbox.</p>
<p>Flexbox allows us to build layouts horizontal or vertical layouts with
relative ease and flexes the contained boxes to fill the area available. But, one of the other cool things about flexbox is that you can rearrange your layout by using the box-ordinal-group property.</p>
<p>For example, here are three boxes, layed out with Flexbox:</p>
<img alt="Flex layout 1" src="https://www.andismith.com/assets/blogs/2012/05/css3-flexbox-carousel/flex1.gif">
<p>To change the order of these boxes, we simply change the box-ordinal-group property on each item. For item 2, I've set the box-ordinal-group property to 1 so that it is first; item 1 is now set to 2, and the third item remains 3.</p>
<pre><code class="language-css">#item2 {
  -moz-box-ordinal-group: 1;
  -ms-box-ordinal-group: 1;
  -webkit-box-ordinal-group: 1;
  -o-box-ordinal-group: 1;
  box-ordinal-group: 1;
}
</code></pre>
<img alt="Flex layout 2" src="https://www.andismith.com/assets/blogs/2012/05/css3-flexbox-carousel/flex2.gif">
<p>This is pretty cool as we no longer have to worry about markup order or floats. And thus my trail of thought moved on to flexbox for carousels.</p>
<p>If you don't wish to read more about how the carousel was made, <a href="http://andismith.github.io/flexbox-carousel/">you can play with it here</a>.</p>
<h2>Implementation</h2>
<p>With a carousel, you probably wouldn't want to flex your items to fill the space (which is ok, as flexing has to be turned on) but we are concerned with the order of the items.</p>
<p>Building a carousel with flexbox is relatively straightforward once you get past the vendor prefixes:</p>
<pre><code class="language-css">#carousel {
  overflow: hidden;
  position: relative;
  width: auto;
}

#carousel ul {
  -moz-box-orient: horizontal;
  -ms-box-orient: horizontal;
  -webkit-box-orient: horizontal;
  -o-box-orient: horizontal;
  box-orient: horizontal;

  display: -moz-box;
  display: -ms-box;
  display: -webkit-box;
  display: -o-box;
  display: box;

  list-style-type: none;
  margin: 0;
  padding: 0;
}

#carousel li {
  height: 200px;
  margin-right: 10px;
  width: 200px;
}
</code></pre>
<p>Here, we are telling our un-ordered list that we wish to display as a flexbox layout on a horizontal plane. (As an aside, I've started placing Opera's vendor prefixes below Webkit's so that if Opera supports the Webkit property, it cascades to receive the O version. Not that Opera supports flexbox at the moment.)</p>
<p>If we maintain an active counter on our carousel, we can change the box-ordinal-group whenever the user clicks next or previous and never have to worry about repeating items again.</p>
<p>The functionality in our JavaScript for changing the ordinal is relatively straightforward, but to ensure we have an element on the left to animate to we need to start our ordinal at the active value-1, and then add a left margin to our carousel that is one item wide.</p>
<pre><code class="language-js">function changeOrdinal() {
  var length = items.children.length,
    ordinal = 0;

  // start at the item BEFORE the active one.
  var index = active - 1;

  /* if the active item was 0, we're now at -1 so
      set to the last item */
  if (index &#x3C; 0) {
    index = length - 1;
  }

  // now run through adding the ordinals
  while (ordinal &#x3C; length) {
    // add 1 to the ordinal - ordinal cannot be 0.
    ordinal++;

    // check the item definitely exists :)
    var item = items.children[index];
    if (item &#x26;&#x26; item.style) {
      // new ordinal value
      item.style[boxOrdinalGroup] = ordinal;
    }

    /* as we are working from active we need to go back to
         the start if we reach the end of the item list */
    if (index &#x3C; length - 1) {
      index++;
    } else {
      index = 0;
    }
  }
}
</code></pre>
<p>I've commented the [code on Github](http://andismith.github.com/flexbox-
carousel/) if you wish to dive deeper in to how it works.</p>
<h2>Summary</h2>
<p>The final flexbox carousel <a href="http://andismith.github.io/flexbox-carousel/">is available to play with on
Github</a>.</p>
<p>Using flexbox comes at a browser support cost, however. The specification was
recently changed, which means most browsers only partially support parts of
Flexbox - but the parts used in this demo work in Chrome, Firefox and Safari.
Flexbox also doesn't work in Opera, or IE9 or below. But perhaps this is a
solution we can use in the future once everything has settled down?</p>
<p>The <a href="https://github.com/andismith/flexbox-carousel">code is available on Github.</a> Enjoy!</p>]]></content:encoded>
      <pubDate>Fri, 25 May 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Complying With The EU Cookie Law</title>
      <link>https://www.andismith.com/blogs/2012/04/comply-with-eu-cookie-law</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/04/comply-with-eu-cookie-law</guid>
      <description>The cookie law is a new EU privacy legislation that requires websites to provide clear and comprehensive information about the cookies being stored; and obtain consent from visitors in order to store or retrieve any information about the user.</description>
      <content:encoded><![CDATA[<p>Recently I've been looking in to how the EU Cookie Law affects web sites
serving users in Europe. The law was introduced into EU legislation in May
2011 and comes into effect in the UK on 26th May 2012. While this law
currently only effects European countries, it is likely that the US will
follow suit and implement a similar law in the years to come.</p>
<p>Disclaimer: I am not a lawyer, not am I a representative from any authority on cookies. These are my interpretations of the cookie law and (like all my blog articles) these views do not represent the views of my employer or associates.
If this subject matter is of interest to you, it is important that you do your own research on the cookie law to ensure your website is complying with the law.</p>
<h2>What is the Cookie Law?</h2>
<p>The cookie law is a new EU privacy legislation that requires websites to
provide clear and comprehensive information about the cookies being stored;
and obtain consent from visitors in order to store or retrieve any information about the user.</p>
<p>In the UK, this becomes law on 26th May 2012 and sites that do not comply
could be fined up to £500,000.</p>
<h2>Types of Cookie</h2>
<p>Before we talk about what cookies are good or bad, let's quickly recap on the different types of cookies. Cookies can either be permanent or session based; and they can either be first party or third party.</p>
<ul>
<li><strong>Permanent Cookies</strong> are cookies which outlast the user's session. So if a user closes their browser or turns off their computer, they will still be remembered when they return to the site.</li>
<li><strong>Session Based Cookies</strong> only last until the user closes the browser window.</li>
<li><strong>First Party Cookies</strong> are cookies which are hosted on the same domain as the website the user is visiting.</li>
<li><strong>Third Party Cookies</strong> are not hosted on the domain the user is currently visiting, but are actually hosted on another domain.</li>
</ul>
<p>Although it is dependent on the functionality the cookie is providing, as a
general rule permanent third party cookies are the most intrusive types of
cookie, whilst session based first party cookies are considered the least
intrusive.</p>
<p>![Intrusiveness of Cookies](http://www.andismith.com/wp-
content/uploads/2012/04/cookie-intrusiveness.gif)</p>
<p>In the browser, there is currently no way to filter out a type of cookie - you can only block all cookies from a particular site.</p>
<p>If you are unsure what types of cookies are present on your website, sites
such as <a href="http://www.cookiecert.com/">CookieCert</a> can help with this process
and quickly give you an understanding of the problem. CookieCert analyses your site to evaluate how many first party, third party, permanent and session based cookies are being used. It also provides a tally on HTML5 and Flash 'cookies' (such as local storage) which could also be considered harmful to users' privacy. Please be aware that CookieCert may not be able capture cookies at all stages of the users journey (such as when the user is logged in).</p>
<p>![Cookie Cert for AndiSmith.com](http://www.andismith.com/wp-
content/uploads/2012/04/cookie-cert.gif)</p>
<p>Browser Developer Tools can also help with a cookie audit. In Chrome and
Safari, click the "Resources" tab to see a list of cookies used on the current site organised by domain. In "Opera", the storage tab provides this
information.</p>
<h2>What Types of Cookies Are Effected?</h2>
<p>So how do you comply with the cookie law in the UK?</p>
<h3>ICO Guidelines</h3>
<p>[The ICO (Information Commissioner's Office) say](http://www.ico.gov.uk/for_or
ganisations/privacy_and_electronic_communications/the_guide/cookies.aspx) that all cookies that do not facilitate the transmission of communication, or are not strictly necessary for a service requested by a user need to be consented to or removed.</p>
<h4>Strictly Necessary Cookies</h4>
<p>Strictly necessary means that storage of or access to information should be
essential to the user journey. The ICO would class the following types of
content as "strictly necessary":</p>
<ul>
<li>Transaction specific (such as shopping baskets)</li>
<li>Security (such as online banking)</li>
</ul>
<h4>Transmission of Communication Cookies</h4>
<p>Cookies for the transmission of communication would include:</p>
<ul>
<li>Stop multiple form submissions</li>
<li>Load balancing</li>
</ul>
<h4>Other Cookies</h4>
<p>All other cookies would require consent from the user. These cookies would
include:</p>
<ul>
<li>Embedded third-party content and social media-plugins</li>
<li>Advertising campaign optimisation</li>
<li>Web analytics / metrics</li>
<li>Personalised content / interface (such as remember me)</li>
</ul>
<h3>GDS Guidelines</h3>
<p>In a strange twist, [the GDS (Government Digital Service) have recently posted a document](http://alphagov.files.wordpress.com/2012/03/gds-cookies-
implementer-guide.pdf) which slightly differs in opinion from the ICO's own
guidelines. They believe the new law is not specifically about cookies, but
about privacy; and that website owners should focus their efforts on the most intrusive types of cookies.</p>
<h4>Moderately Intrusive Cookies</h4>
<p>The GDS class the following types of content as moderately intrusive (and
therefore requiring attention):</p>
<ul>
<li>Embedded third-party content and social media-plugins</li>
<li>Advertising campaign optimisation</li>
</ul>
<h4>Minimally Intrusive Cookies</h4>
<p>The following would be minimally intrusive:</p>
<ul>
<li>Web analytics / metrics</li>
<li>Personalised content / interface (such as remember me)</li>
</ul>
<h4>Exempt Cookies</h4>
<p>The following would be exempt from changes to privacy regulations:</p>
<ul>
<li>Stop multiple form submissions</li>
<li>Load balancing</li>
<li>Transaction specific (such as shopping baskets)</li>
<li>Security (such as online banking)</li>
</ul>
<h3>How Does This Affect Analytics?</h3>
<p>The [ICO guidelines document (PDF)](http://www.ico.gov.uk/news/latest_news/201
1/~/media/documents/library/Privacy_and_electronic/Practical_application/guidance_on_the_new_cookies_regulations.ashx) states on page 25 that:</p>
<blockquote>
<p>Although the Information Commissioner cannot completely exclude the
possibility of formal action in any area, it is highly unlikely that priority for any formal action would be given to focusing on uses of cookies where there is a low level of intrusiveness and risk of harm to individuals, if an organisation can demonstrate they have done everything they can clearly to inform users about the cookies in question and to provide them clear details of how to make choices. Whilst he does not consider they are exempt from the rules the Commissioner is therefore unlikely to prioritise, for example, first party cookies used for analytical purposes and cookies that support the accessibility of sites and services, in any consideration of regulatory action.</p>
</blockquote>
<p>So assuming your analytics are first party (that is, they set a cookie on your domain and not a different domain), this should apply.</p>
<h3>So, What is Affected?</h3>
<p>Based on this information, it is reasonable to assume that sites with embedded third-party content and social media-plugins and/or advertising campaign optimisation are going to be the most affected by this law. However, even the least intrusive types of cookies require some effort to achieve compliance.</p>
<h2>7 Steps To Comply</h2>
<p>There are a large number of points that need to be considered with the new law and each website needs to be considered on a case by case basis.</p>
<p>The below steps are numbered in order of implementation difficulty and impact but are by <em>no means a comprehensive guide on how to comply</em>.</p>
<h3>Step 1. Carry out a Cookie Audit</h3>
<p>The first, and easiest step, is to carry out an audit of your website. That
is, create a document listing each cookie used on your website together with the following information about it. Your audit could include the following fields:</p>
<ul>
<li><strong>Cookie Name</strong> - The name used in implementation (e.g. UID)</li>
<li><strong>Cookie Friendly Name</strong> (e.g. Username)</li>
<li><strong>Description</strong> - The description should provide as much detail about the purpose of the cookie as possible.</li>
<li><strong>Potential Intrusiveness to User</strong> - Using a guide such as the one provided by the GDS (shown above), each cookie should be rated for its intrusiveness.</li>
<li><strong>Expiry</strong> - The number of days it takes for the cookie to expire.</li>
</ul>
<h3>Step 2. Updated Your Privacy Policy</h3>
<p>Make sure that your privacy policy has a clear section on cookies and how your site uses them. If you want to be 100% transparent, you could include the findings of your cookie audit on these pages, as
<a href="http://www.dominos.co.uk/Campaign/about/Cookies.html">Dominos</a> and <a href="https://www.gov.uk/help/cookies">the UK
Government</a> do.</p>
<p>![Dominos' Cookie Audit](http://www.andismith.com/wp-content/uploads/2012/04
/cookies-dominos.jpg)</p>
<h3>Step 3. Include Clear Links to Cookie Information</h3>
<p>Ensure that you have included a clear link to cookie information on every
page. For example, if you currently have a link to "privacy policy" and "terms and conditions" in your footer, you could add a third link directing users to "cookie information". If information about cookies exists on the same page as your privacy policy, use a hash tag to jump the user to this information.
<a href="http://www.amazon.co.uk/">Amazon UK</a> and <a href="https://www.gov.uk/">Gov UK</a> have a good example of this.</p>
<h3>Step 4. Remove Unnecessary Cookies</h3>
<p>Now you've completed your cookie audit, if there are any unnecessary cookies remove them. Try to keep your cookie list to as few as possible.</p>
<h3>Step 5. Examine Expiry Dates</h3>
<p>For each cookie in your cookie audit, examine the cookie expiry dates and
check that they are reasonable. For example, a cookie which remembers a user for two weeks would be considered reasonable, whilst a cookie that remembers the user for 20 years would not. There should be no reason to have a cookie that lives that long, so shorten its lifespan.</p>
<h3>Step 6. Ask For Consent At User Interaction Points</h3>
<p>There are points in the user journey where it is possible to ask for consent to use cookies. For example, when a user logs in and ticks the "remember me" checkbox - including an additional line of text to inform the user checking this box will also mean they agree to being remembered won't affect your user journey but will bring additional compliance.</p>
<h3>Step 7. Alert the User About Cookies</h3>
<p>If you do still require use of the most intrusive types of cookies, then your best bet is to provide an alert to the user about cookies. <a href="http://www.ico.gov.uk/">The ICO use one of these on their own site</a> but it is visually displeasing.</p>
<p>Reading further in to cookie law, [page 16 of the ICO guidelines (PDF)](http:/
/www.ico.gov.uk/news/latest_news/2011/~/media/documents/library/Privacy_and_el
ectronic/Practical_application/guidance_on_the_new_cookies_regulations.ashx) states that:</p>
<blockquote>
<p>Many websites routinely and regularly use pop ups or ‘splash pages’ to make users aware of changes to the site or to ask for user feedback. Similar techniques could, if designed well enough, be a useful way of highlighting the use of cookies and obtaining consent. Using this technique you could ensure you are compliant by not switching on any cookies unless the person clicks I agree. Some users might not click on either of the options available and go straight through to another part of the site. If they do, you might decide that you could set a cookie and infer consent from the fact that the user has seen a clear notice and actively indicated that they are comfortable with cookies by clicking through and using the site.</p>
</blockquote>
<p>In other words, as long as you provide a clear message to the user about
cookies and give them a chance to decline, by using your site you can infer
consent. This is the approach <a href="http://www.qubitproducts.com/">Qubit</a> have
taken.</p>
<p>![BT's impressive settings page](http://www.andismith.com/wp-
content/uploads/2012/04/cookie-settings.jpg)</p>
<p>But by far the best implementation I've seen of a cookie prompt is
<a href="http://www.bt.com">BT.com</a> who prompt the user with a pop-up in the bottom
right hand corner. Pressing the "Settings" button then allows the user to
configure what cookies will be used.</p>
<h2>Conclusion</h2>
<p>As I stated at the beginning of the article, if you need to know more about
cookies and the law then it really is best to do individual research and
involve your companies legal team.</p>
<p>The most important point to take from this article is that it is vital that
you take action on your corporate sites to avoid being penalised, and I hope the information listed above can help guide you with that.</p>
<p>The two documents I have referred to in this article are available at:</p>
<ul>
<li><a href="http://www.ico.gov.uk/news/latest_news/2011/~/media/documents/library/Privacy_and_electronic/Practical_application/guidance_on_the_new_cookies_regulations.ashx">Link to the ICO document (PDF).</a></li>
<li><a href="http://alphagov.files.wordpress.com/2012/03/gds-cookies-implementer-guide.pdf">Link to the GDS document (PDF).</a></li>
</ul>
<p>Additional resources include:</p>
<ul>
<li><a href="http://www.cookielaw.org/">The Cookie Collective</a></li>
<li><a href="http://silktide.com/cookielaw">SilkTide</a></li>
</ul>]]></content:encoded>
      <pubDate>Thu, 19 Apr 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>My JS1k 2012 Entry</title>
      <link>https://www.andismith.com/blogs/2012/02/my-js1k-2012-entry</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/02/my-js1k-2012-entry</guid>
      <description>This year I decided to enter the JS1k competition for the first time. The idea behind JS1k is to produce a cool JavaScript application in under 1kb (1024bytes) of code. I had not done any JavaScript golfing before, so I thought it would be a nice little challenge for myself. I didn&apos;t realise at the time quite how addictive or time-consuming JavaScript golfing was going to be!</description>
      <content:encoded><![CDATA[<p>This year I decided to enter the JS1k competition for the first time.</p>
<p>The idea behind JS1k is to produce a cool JavaScript application in under 1k
(1024bytes) of code. I had not done any JavaScript golfing before, so I
thought it would be a nice little challenge for myself. I didn't realise at
the time quite how addictive or time-consuming JavaScript golfing was going to
be!</p>
<h2>My Entry</h2>
<p>My entry in to the competition is a jetpack style game. As the theme of this years js1k competition was 'love', the game is called "Cupid's Heart
Collection" and revolves around the user playing as Cupid who has to fly
through his love cavern collecting hearts and avoiding spikes. As you venture further in to the cave, things speed up. Your final score is based on <code>Distance * Number of Hearts collected</code>.</p>
<p><a href="http://js1k.com/2012-love/demo/1190">You can play it here!</a></p>
<div class="post-image">
<img alt="Cupid" src="cupid2-small.jpg" srcset="cupid2-small.jpg 640w, cupid2-medium.jpg 1024w, cupid2-large.jpg">
</div>
<p>I had originally intended to create a Canabalt style game for my entry. For
completely illogical reasons I started with the parallax background - two
arrays containing random numbers to determine the height of the blocks before I eventually joined them together to save space.</p>
<p>By the time I had created the parallax background I found myself with under
half my space left, so my entry became a jet pack game. I wanted to make sure there was more than just avoidables, and hearts seemed like a perfect fit based on the competitions theme. I used character for both the heart and
spike.</p>
<p>I also wanted my character to be more than a square - Cupid seemed to fit
perfectly for the theme, and using the letters C, U, P, I, D actually gave me the basis for a character design. C for the head, U for the body, P and I for the legs and D for the wings.</p>
<p>Once I had finished, I found myself at around 1800 bytes. I managed to reduce the amount to around 1400 bytes before I introduced gradients. Gradients in canvas take up a lot of bytes thanks to names like 'createLinearGradient' and 'addColorStop'. But I was determined to make them fit. I was also determined I wasn't going to cut anything. Some of the techniques used are listed below.</p>
<p>And so began a process of working hard to reduce the number of bytes to 1,024. It was addictive, too addictive. I found myself writing code until the early hours of the morning on several occasions. And every time I figured out a way of reducing the bytes to slightly below 1,024, I decided to add a new feature. Which meant further reduction. Simple things like giving a final score, turning Cupid red and even restarting the game without a page refresh were added very late in the process.</p>
<p>1k doesn't look like much code, but it certainly took a long time to create.
Every time a byte was saved it became more difficult to remove another.
Golfing continued right up until submission with the 20 character limit on
submission names meaning I had to use the heart symbol for the title.</p>
<h2>Techniques I Used</h2>
<p>There are so many useful resources available for JavaScript golfing now. It's just a case of figuring out when to apply them. I learnt so much doing this exercise, most of it couldn't be used in production code - but I feel closer to JavaScript than I ever have before.</p>
<p><strong>Resources:</strong></p>
<ul>
<li><a href="https://github.com/jed/140bytes/wiki/Byte-saving-techniques">140 bytes Byte Saving Techniques</a></li>
<li><a href="http://codegolf.stackexchange.com/questions/2682/tips-for-golfing-in-javascript">Stack Exchange Tips for Golfing</a></li>
<li><a href="http://benalman.com/news/2010/08/organ1k-js1k-contest-entry/">Ben Alman's blog</a></li>
<li><a href="http://marijnhaverbeke.nl/js1k/">Marijn Haverbeke's blog</a></li>
</ul>
<p><strong>Techniques:</strong></p>
<ul>
<li>Renaming methods within the canvas context to 2 letters. I played around with a few different letter combos, but the best was using the first and sixth letter (which Marijn had also found) - I had to take a hit on .rotate because of this, but other options yielded worse results. I didn't worry about the 7th letter being undefined as I didn't need any of those methods.</li>
<li>Reducing the number of times I called function - I originally had around 6 functions, but most of these became 1 loop in the end. Using variables meant I could call pieces of code within the loop at different times.</li>
<li>Declaring variables by including them in the function arguments - this helped me a lot</li>
<li>Changing values to the same as other variables to reduce declaration size.</li>
</ul>
<h2>If I had more bytes..</h2>
<p>I really wanted to include a particle engine in the game, where the hearts
would explode on collection and lava would jump out of the floor - but I just didn't have enough space. In reality, the game sometimes feels like it jerks a bit at the higher speeds so the biggest consideration with more bytes would have to be performance; and adding a pause so the game doesn't instantly restart if the player holds down a key for too long.</p>
<p><a href="http://js1k.com/2012-love/demo/1190">Play "Cupid's Heart Collection" here!</a></p>]]></content:encoded>
      <pubDate>Mon, 27 Feb 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>CSS3 Backgrounds of the Future</title>
      <link>https://www.andismith.com/blogs/2012/02/css-backgrounds-of-the-future</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/02/css-backgrounds-of-the-future</guid>
      <description>Whilst we&apos;ve been busy supporting older versions of Internet Explorer, a few new CSS features for backgrounds have sprung out of the woodwork and in to our browsers.</description>
      <content:encoded><![CDATA[<p>Whilst we've been busy supporting older versions of Internet Explorer, a few new CSS features for backgrounds have sprung out of the woodwork and in to our browsers.</p>
<ul>
<li><code>background-size</code></li>
<li><code>background-clip</code></li>
<li>and <code>background-origin</code></li>
</ul>
<p>These features have been hiding behind their more popular gradient background
color cousins; but are worth making a note of, especially if you're creating
sites for mobile phones and recent browsers; and the best news is that they
are completely prefix-free!</p>
<p>Let's take a look at what these properties are, what they do and afterwards
we'll have some fun with animating backgrounds. Yay!</p>
<p>To demonstrate how these features work, I've put together some demos. For each demo, unless otherwise stated the following CSS background properties are set:</p>
<ul>
<li><code>background-image: red url(img/ice.jpg)</code></li>
<li><code>background-position: top left (default)</code></li>
<li><code>background-repeat: no-repeat</code></li>
</ul>
<p>Please note, as these are live demos, you will need Firefox 4+, Internet
Explorer 9+, Opera 10.5+, Safari 3+ or any Chrome to view them.</p>
<h2>background-size</h2>
<p>If you've been developing for mobile phones with high density pixels, you may have already come across <code>background-size</code>. For a long time on the web we were unable to resize images when they were used as a background but with CSS3 property <code>background-size</code>, not only are we able to specify the size of a background we are able to make it relative to the containing element.</p>
<p>Here's the syntax and the possible options:</p>
<pre><code class="language-css">background-size: auto | cover | contain | value [value];
</code></pre>
<p>To show how each option works, let's use some examples:</p>
<h3>auto, contain and cover</h3>
<ul>
<li>
<p><code>background-size:auto;</code></p>
</li>
<li>
<p><code>background-size:contain;</code></p>
</li>
<li>
<p><code>background-size:cover;</code></p>
</li>
</ul>
<p>You'll already be familiar with <code>background-size: auto</code> (the example on the
left). This is the default setting for background-size and what almost every background on the web so far uses. This setting doesn't resize your image at all - if it's too big it'll cut off a chunk of the image. If it's too small it'll either leave a gap or repeat, depending on the value of your <code>background-repeat</code>.</p>
<p>With <code>background-size:contain</code> (middle), the background image will be resized so that the entire image fits in the space whilst keeping its original proportions. This will mean that unless your container is to the same proportion as your background image there will be a gap. In our case it's at the bottom and in red as the background-color has filled in the gap for us.</p>
<p>If you can't guarantee the size of the background and you don't want a gap,
<code>background-size:cover</code> may be exactly what your looking for (on the right).
This option will resize the image so that the shortest dimension (width or
height) exactly fits the space whilst keeping the images original proportion.
What this means is part of your image will be cut off. Depending on the
proportions of the image will depend on how much.</p>
<p>So to recap:</p>
<ul>
<li>Use <strong>contain</strong> and your background image height and width will be &#x3C;= to the container.</li>
<li>Use <strong>cover</strong> and your background image height and width will be >= to the container.</li>
</ul>
<h3>Percentages</h3>
<ul>
<li>
<p><code>background-size:100%;</code></p>
</li>
<li>
<p><code>background-size:50%;</code></p>
</li>
<li>
<p><code>background-size:100% 100%;</code></p>
</li>
</ul>
<p>Now we've covered the three keywords, it's time to take a look at the other
values that can be placed in to <code>background-size</code>. All values can either be
specified as a single value or two separate values - one for the width and one
for the height.</p>
<p>In the case where only one percentage value is specified, <code>background-size</code>
will use the value specified to resize the width regardless of whether the
width or height is the bigger dimension, which is why in our first example
(above left) there is a small gap at the bottom of the container.</p>
<p>Using percentage values allow us to specify what the percentage of the
containing element the image appears in - NOT what percentage of the overall
image dimensions we wish to show. In our middle example above the image is 50%
of the total container. Again, the 50% is taken from the width value.</p>
<p>Even though the images are appearing at a smaller size, the image is still
loaded from the server at its full size - resizing is occuring client-side, so
the browser will still have to download the full size image.</p>
<p>We can use multiple values to specify different dimensions for the width and
height. In the case of our example above on the right, we have specified a
width of 100% and a height of 100%. This is not the same as cover mode - as we
are specifying both dimensions the image will be resized disproportionally,
which depending on the image and the values used may or may not be noticeable.</p>
<ul>
<li>
<p><code>background-size: 50% 100%;</code></p>
</li>
<li>
<p><code>background-size: 150%;</code></p>
</li>
<li>
<p><code>background-size: 150%;</code></p>
</li>
</ul>
<p>background-position:center</p>
<p>As you can see in our first example here the image has been disproportionally
stretched when I've specified one dimension as half of the other. Much more
noticeable!</p>
<p>In our second two examples, a percentage value larger than 100% is used. In
these examples, the image is shown as 150% bigger than the containing element
(again, not 150% of the image). The third example here also centers the image,
as it's entirely possible to use all the other background css properties
whilst using <code>background-size</code>.</p>
<ul>
<li>
<p><code>background-size:50%; background-position:right bottom;</code></p>
</li>
<li>
<p><code>background-size:50%; background-position:center;</code></p>
</li>
<li>
<p><code>background-size:50%; background-repeat:repeat;</code></p>
</li>
</ul>
<p>The first two examples here use <code>background-position</code> to change the
positioning of the background image. Don't forget you can use percentages and
other values for positioning images if you wish.</p>
<p>In our third example, we are repeating the image, which creates a tile effect
even though it has been resized to 50% of the size of the original image.</p>
<p>Percentages are very useful, but sometimes you want to be explicit with the
size of your background image. Let's continue.</p>
<h3>Length values</h3>
<ul>
<li>
<p><code>background-size:80px auto;</code></p>
</li>
<li>
<p><code>background-size:auto 200px;</code></p>
</li>
<li>
<p><code>background-size:80px 200px;</code></p>
</li>
</ul>
<p>The most common length values are pixels (px), but you could use other valid length measurements if you wish. In the first example (left), <code>background-size</code> is set to 80px auto; which means that the width will be set to exactly 80px and the height will be proportionate to that value. In this example, the auto value is actually superfluous - the default is already auto so it is not required. In our second example (middle), we have set the width to auto and the height to 200px which means the width will be set in proportion to the height. In our final <code>background-size</code> example, we've stretched the image by specifying a width and height that isn't suited for the image.</p>
<h3>Multiple backgrounds</h3>
<p>If you're using multiple backgrounds, you can have multiple background sizes by separating the values with a comma.</p>
<ul>
<li>
<p>background-image:url(img/bg-grid.gif),url(img/ice.jpg);<br>
background-size:auto, 50%;</p>
</li>
<li>
<p>background-image:url(img/bg-grid.gif),url(img/ice.jpg);<br>
background-size:60%, 100%;</p>
</li>
<li>
<p>background-image:url(img/bg-grid.gif),url(img/ice.jpg);<br>
background-size:32px 100%, 90% 150px;</p>
</li>
</ul>
<p>These examples have different background sizes for each image - simply list
the background sizes in the same order as your images. Remember that multiple images are stacked with the first listed image at the top and the last listed image at the bottom (top to bottom).</p>
<p>The third example (right) mixes percentages and lengths to create a cocktail of <code>background-size</code> goodness.</p>
<p>Let's move on to <code>background-clip</code>, but if you're interested you can [read
more about background size on MDN](https://developer.mozilla.org/en/CSS
/background-size).</p>
<h2>background-clip</h2>
<p><code>background-clip</code> allows us to specify what area of the container we wish to clip our backgrounds to. There are three options available: border-box,
padding-box and content-box.</p>
<pre><code class="language-css">background-clip: border-box | padding-box | content-box</code></pre>
<ul>
<li>
<p>background-clip:border-box;<br>
(this is the default, you don't need to specify it!)</p>
</li>
<li>
<p>background-clip:padding-box;</p>
</li>
<li>
<p>background-clip:content-box;</p>
</li>
</ul>
<p><code>border-box</code> (left) is the default option, and this means the background will
sit under the border. This isn't noticeable unless you have a transparent,
semi transparent or semi solid border (as we have here). As you can see in our
example on the left, the background sits under the border. If you're wondering
why the background sits under the border on the bottom right, but not the top
left; it's due to the <code>background-origin</code>, which I'll explain shortly.</p>
<p><code>padding-box</code> (middle) clips the image so it no longer appears under the
border. The background image will start at the top left of the inside of the
containing element and finish at the bottom right of the containing element.</p>
<p><code>content-box</code> (right) clips the image to the area where the content can sit.
That is - the background begins where the padding finishes. In the case of our
example, the padding is 5px wide all the way round so this creates a 5px
clipping border.</p>
<p><a href="https://developer.mozilla.org/en/CSS/background-clip">More information on background-clip is available on
MDN</a>.</p>
<h2>background-origin</h2>
<p>The final CSS3 background property I wanted to cover was <code>background-origin</code>.
As you would have noticed from our example above, the background for the
border-box didn't sit under the left and top borders. The reason for this is
because of the background-origin which determines where the background should
start - and it's default value is padding-box.</p>
<pre><code class="language-css">background-origin: padding-box | border-box | content-box;</code></pre>
<ul>
<li>
<p>background-origin:padding-box;</p>
</li>
<li>
<p>background-origin:border-box;</p>
</li>
<li>
<p>background-origin:content-box;</p>
</li>
</ul>
<p>As before, <code>padding-box</code> starts our background at the point where padding
begins.</p>
<p><code>border-box</code> allows us to start our background where the border starts - at
the top left of the container element.</p>
<p><code>content-box</code> starts our background where our content begins (after the
padding). If you look back to our example of <code>background-clip:content-box</code> you
may notice that the image in our container starts 5px after it does in this
example. This is because background-origin determines where an image starts,
not background-clip.</p>
<p><a href="https://developer.mozilla.org/en/CSS/background-origin">There's more information on background-origin at
MDN</a>!</p>
<h2>Animations</h2>
<p>Now we're done with learning the properties, let's have some fun with
background animations using CSS3 transitions and animations! There's nothing
really to learn in this section, I was just playing around. If you want to
know more about CSS3 transitions and animations there are lots of useful
resources already out there - Google is your friend!</p>
<p>Of the three CSS3 properties we've covered today, only <code>background-size</code> can
be animated. However, <code>background-color</code> and <code>background-position</code> can also be
animated, as you'll see below (providing your browser supports animation -
sorry Opera/IE9 users).</p>
<ul>
<li>
<p>Hover to zoom!</p>
</li>
<li>
<p>Hover to move background!</p>
</li>
<li>
<p>Hover to move background!</p>
</li>
</ul>
<p>The first example uses <code>background-size</code> to increase the size of the image on
hover. The second (middle) example actually uses <code>background-repeat</code> and
<code>background-position</code> to create a film reel effect. The third example uses
<code>background-size</code> again to zoom in from a small image.</p>
<ul>
<li>
<p>Hover to change color!</p>
</li>
<li>
<p>Hover to change size!</p>
</li>
<li>
<p>Hover to Mother Effing Rainbow!</p>
</li>
</ul>
<p>In these examples, the first example (left) changes the background color from
red to blue on hover; the second example uses both width and height background
sizes to go from a lamppost to a letterbox effect; and the third example is a
Mother Effing Rainbow effect in tribute to
<a href="http://mothereffingtextshadow.com/">mothereffingtextshadow.com</a>.</p>
<p>Enjoy the animations, and I hope this has been useful to you! Thanks for
reading!</p>]]></content:encoded>
      <pubDate>Mon, 13 Feb 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>jQuery UK 2012 Conference</title>
      <link>https://www.andismith.com/blogs/2012/02/jquery-uk-2012-conference</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/02/jquery-uk-2012-conference</guid>
      <description>Yesterday I travelled to Oxford to attend the jQuery UK conference, which the team at White October carefully crafted together. The snow threatened to stop me, but thankfully calmed down by the morning. So a quick hop, skip and jump on to a train and coach; and I found myself in Oxford at the Saïd Business School.</description>
      <content:encoded><![CDATA[<p>I wanted to write up my notes on the jQuery UK Conference as I had an amazing time. Apologies to anybody who follows me on Twitter who didn't want to know about the conference, you were probably spammed a bit. But shame on you for not wanting to know!! This isn't a concise write-up of everything that happened, but is more what I took from the conference (what I learnt/found relevant to me .etc)</p>
<p>Yesterday I travelled to Oxford to attend the jQuery UK conference, which the team at White October carefully crafted together. The snow threatened to stop me, but thankfully calmed down by the morning. So a quick hop, skip and jump on to a train and coach; and I found myself in Oxford at the Saïd Business School.</p>
<div class="post-image">
<img alt="Snowy Oxford" src="oxford-small.jpg" srcset="oxford-small.jpg 640w, oxford-medium.jpg 1024w, oxford-large.jpg">
</div>
<p>After discovering I was pretty much the only person who hadn't won a
Blackberry Playbook, the White October team kicked off the conference by introducing themselves and their keynote speaker Ralph Whitbeck.</p>
<h2>Ralph Whitbeck</h2>
<p>After promising the UK edition of the jQuery conference would become a yearly experience, Ralph (aka <a href="http://www.twitter.com/redwolves">@RedWolves</a>) spoke about how jQuery's popularity had increased and who the current team are
before explaining what changes had happened to jQuery in the past year. He went through the changes in version 1.5; 1.6 and 1.7 - most of which sounded familiar, but some of which had passed me by.</p>
<h3>When and Then</h3>
<p>Version 1.5 introduced <code>$.when</code> and <code>$.then</code> which I haven't really looked into before, but the basic syntax is:</p>
<pre><code class="language-javascript">$.when(deferreds).then(successFunction, failFunction);
</code></pre>
<p>So, for example, if you loading a page or data through AJAX you could do:</p>
<pre><code class="language-javascript">$.when($.ajax("myUrl.json")).then(doSomething, handleError);
</code></pre>
<h3>Relative CSS</h3>
<p>In Version 1.6, I was unaware that it was now possible to use jQuery's
<code>.css()</code> function to change values relatively. For example:</p>
<pre><code class="language-javascript">$("#myElement").css("width", "+=100px");
</code></pre>
<h3>On and Off, and AMD</h3>
<p>In Version 1.7, jQuery <code>.on()</code> and <code>.off()</code> was introduced, <a href="/blog/2011/11/on-and-off/">which I've
written about before</a>; and jQuery introduced AMD
support which you can <a href="https://github.com/amdjs/amdjs-api/wiki/jQuery-and-AMD">read more about on the amd-js api Github
page</a>.</p>
<p>Ralph also spoke about the internal workings of jQuery and how we could be
more involved with. jQuery has <a href="http://jquery.org/updates">a status updates page</a> which is updated by each sub-team of jQuery every Friday and <a href="http://jquery.org/meetings">a jQuery meetings page where you can find out what meetings are forthcoming</a>.</p>
<p>There was also talk of 1.8 and beyond. The main focus at the moment is to cut down the size of the library and clean up the codebase. jQuery 1.8′s focus is this, and is unlikely to contain any new APIs. It's possible that jQuery may drop IE6/7 support in to a plugin in the future. At the moment, this is just being discussed and is not official - just an idea. As Christian Heilmann pointed out on Twitter, jQuery's main purpose is to support cross-browser functionality and without IE6/7 it starts to reduce the need for using jQuery at all.</p>
<p>Going forwards, jQuery's site is going to see an infrastructure overhaul and site refresh. There will be a new plugin site, this time with backups - and a new learn site, which sounds like something I'd like to get involved with.</p>
<p>[Ralph's slides are available online](http://speakerdeck.com/u/rwhitbeck/p
/state-of-jquery-jquery-conference-2012-uk).</p>
<h2>Todd Parker</h2>
<p>Next up was Todd Parker (<a href="http://www.twitter.com/toddmparker">@toddmparker</a>)
to talk about jQuery Mobile. As someone who has spent the last 1 and a half
months working with jQuery Mobile and PhoneGap trying to get an animation
heavy Android application up and running; it was refreshing to hear that
everything we had encountered was being spoken about. At the start of the
presentation, Todd asked everyone what phone they used as their primary
device. Over 60% of the room had Android devices; most others had iOS; with a few having BlackBerries, Windows Phones .etc. Interestingly, there only seemed to be 1 person using a Nokia, but I'm sure I saw at least 3 people from Nokia at the conference.</p>
<div class="post-image">
<img alt="Supported by jQuery Mobile" src="devices-small.jpg" srcset="devices-small.jpg 640w, devices-medium.jpg 1024w, devices-large.jpg">
</div>
<p>jQuery Mobile is awesome - a great tool for mobile development. When I first used jQuery Mobile I wasn't sure. I was never a fan of jQuery UI - it always felt like too much bloat, unless you were using the majority of its features.
jQuery Mobile felt the same the first time I used it. It changes your DOM -
which really made me unhappy; but after playing around with it for a while you begin to understand why, and see how much help it is giving you by making things work on multiple devices; so then you start to play jQuery Mobile to its powers.</p>
<p>But for anyone considering building an Android application which uses the
default browser (like PhoneGap is forced to do), the browser sucks. It doesn't do what you need. It actually turned out we'd managed to force Android to do more effects than was claimed on the presentation (thanks to sheer determination to find a workable solution), but generally throughout the conference it was advised not to develop Android web apps to package natively.</p>
<h3>The future of jQuery Mobile</h3>
<p>Todd revealed some awesome changes coming to jQuery Mobile. Version 1.1, which is due at the end of February should feature a better loader; new transitions (and a default of fade for Android because it sucks at everything else); fixed toolbars; data-mini - which allows smaller buttons and text (which can be placed in headers for example); a fix to the iOS orientation bug where content is cut off when changing device orientation; and most importantly (for me), data-enhance="false" which will tell jQuery mobile to stop screwing with your DOM for all child elements. They will be left alone. <a href="http://jquerymobile.com/test/">jQuery Mobile v1.1 is available online for testing already</a>.</p>
<p>Version 1.2 also promises some interesting features such as Fetchlinks which allow you to specify a data-target for a link - and jQuery Mobile will load content in to this container.</p>
<p>[Todd's slides are available online](http://speakerdeck.com/u/toddparker/p
/jquery-mobile-keynote-uk-conference).</p>
<h2>Dion Almaer and Ben Galbraith</h2>
<p>After a quick break, Dion Almaer (<a href="http://www.twitter.com/dalmaer">@dalmaer</a>)
and Ben Galbraith (<a href="http://www.twitter.com/bgalbs">@bgalbs</a>) were up on stage and talking about Native vs Web applications. It's clear from their
presentation that Dion and Ben know each other well and work together just by how they could easily alternate speaking roles - I've done that before and it's a tricky thing to get right, but they nailed it.</p>
<p>They had several learnings in this area, first talking about native vs web
applications and their advantages and disadvantages and how the Android
browser meant that it wasn't really ready for animation rich web based
applications yet - which is interesting as my recent work found that although it is an absolute pain in the ass, if you spend enough time tinkering it is possible to get something <em>relatively</em> nice animating on Android.</p>
<p>Dion and Ben have released a couple of tools to help with their development.
<a href="http://walmartlabs.github.com/thorax/">Thorax</a> is an opinionated application framework built upon Backbone.js; whilst
<a href="http://walmartlabs.github.com/lumbar/">Lumbar</a> is a build tool that generates modular platform specific applications.</p>
<h2>Jorn Zeafferer</h2>
<div class="post-image">
<img alt="Jorn on Stage" src="jorn-small.jpg" srcset="jorn-small.jpg 640w, jorn-medium.jpg 1024w, jorn-large.jpg">
</div>
<p>You may not have heard of Jorn
(<a href="http://www.twitter.com/bassistance">@bassistance</a>) before, but you have
probably used his products - Jorn has created a bunch of jQuery plugins,
including the [awesome jQuery Validate plugin](http://bassistance.de/jquery-
plugins/jquery-plugin-validation/). Today Jorn spoke about the pitfalls and
opportunities of single page web applications.</p>
<p>After going through some advantages and disadvantages, Jorn talked about the JavaScript History API, showing us the second biggest shocker of the day* - the History API browser support matrix, and more specifically how Android used to support it, but now doesn't! (See <a href="">Can I Use?</a> for more info).</p>
<div class="post-image">
<img alt="Android Support" src="android-support-small.jpg" srcset="android-support-small.jpg 640w, android-support-medium.jpg 1024w, android-support-large.jpg">
</div>
<p>Like all good web features, there are polyfills available -
<a href="http://html5please.us">html5please.us</a> is a great resource for finding out
whether you should use what you want to use and whether you need to use any
polyfills.</p>
<ul>
<li>The biggest shocker of the day was that around half the attendees hadn't read <a href="http://www.amazon.co.uk/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742/">JavaScript: The Good Parts</a>. If you are one of those people who hasn't, it's <em>really</em> worth reading.</li>
</ul>
<h2>Lunch</h2>
<p>Next up was lunch, and two lunch time talks. I missed Wes Nolte's
(<a href="http://www.twitter.com/wesnolte">@wesnolte</a>) 10 jQuery tips talk as I was
eating lunch (sorry Wes). The QUnit talk by Laurent Delcambre
(<a href="http://www.twitter.com/tquila_laurent">@tquila_laurent</a>) seemed like a good introduction to using QUnit. Laurent explained that using 'same' is preferable to using 'equals' for the same reason using === is preferable to using == (you are checking for same type as well as value so don't get <a href="http://james.padolsey.com/javascript/truthy-falsey/">truthy/falsey results</a>). Also, 'same' checks the contents of your object.</p>
<p>At this point I won a book on JavaScript, which at 1080 pages is going to take some time to read; but was awesome.</p>
<h2>Christian Heilmann</h2>
<p>To be honest, Christian Heilmann's
(<a href="http://www.twitter.com/codepo8">@codepo8</a>) talk was so captivating I didn't write any notes. Chris is an excellent speaker, and his talk was perfectly pitched to the audience he was speaking to - some JavaScript developers, some people that use jQuery (yes, they are different). Interestingly for a jQuery Conference, Chris' talk was about how we should stop relying so much on jQuery, especially on mobile devices.</p>
<p>On mobile phones, we do not need to worry about old versions of Internet
Explorer; and including the jQuery library adds a chunk of extra downloading over 3G.</p>
<div class="post-image">
<img alt="Chris&#x27; Talk" src="chris-small.jpg" srcset="chris-small.jpg 640w, chris-medium.jpg 1024w, chris-large.jpg">
</div>
<p>Chris spoke about two things that stuck firmly in my mind - one I was aware
of, the other I was not.</p>
<h3>Selecting elements with JavaScript (the simple way)</h3>
<p><code>document.querySelector( selector )</code> allows us to grab the first element on
the page that matches our CSS selector (<a href="https://developer.mozilla.org/En/DOM/Document.querySelector">read more on
MDN</a>).<br>
Use <code>document.querySelectorAll( selector )</code> to return all matching elements
(<a href="https://developer.mozilla.org/en/DOM/document.querySelectorAll">read more on
MDN</a>).</p>
<p>This functionality is supported in <a href="http://caniuse.com/#search=querySelector">all browsers except IE6 and
IE7</a>, so if you do not need to
support these browsers (e.g. on mobile) you can use this functionality.</p>
<h3>Using classList</h3>
<p><code>element.classList</code> was new to me. classList basically replaces the need for
jQuery's addClass, removeClass and hasClass by providing this functionality
natively.</p>
<p>To add a class from an element you use:</p>
<pre><code class="language-javascript">element.classList.add("my-class");
</code></pre>
<p>To remove a class from an element you use:</p>
<pre><code class="language-javascript">element.classList.remove("my-class");
</code></pre>
<p>To check whether an element has a particular style you can use:</p>
<pre><code class="language-javascript">element.classList.contains("my-class");
</code></pre>
<p>There is also functionality to toggle a class, so you do not need to check
whether it exists before adding or removing it (very handy):</p>
<pre><code class="language-javascript">element.classList.toggle("my-class");
</code></pre>
<p><code>classList</code> has <a href="http://caniuse.com/#search=classList">less browser support</a>
than querySelector but looks as though it could be incredibly handy in the
future. (<a href="https://developer.mozilla.org/en/DOM/element.classList">read more on
MDN</a>)</p>
<p>Chris' big plea to his audience was to not just answer everything with "we'll use jQuery" - which was very fitting for the audience (considering half of them hadn't read The Good Parts). Keep animations in CSS where possible, keep logic in JavaScript - jQuery shouldn't be the solution to everything. It's very true, and a very big problem I find when interviewing developers.</p>
<p><a href="http://icant.co.uk/talks/jquk/">Chris' slides are available online</a>. Chris
also had the honour of being the first speaker to use a 'to-do' list example.</p>
<h2>Haymo Meran</h2>
<p>Talking after Chris was always going to be a challenge, and to be honest
Haymo's (<a href="http://www.twitter.com/draftkraft">@draftkraft</a>) talk seemed more
like a sales pitch than anything. I generally dislike WYSIWYG editors but the Aloha Editor looks very cool. It uses 'contenteditable' (which can be a
absolute pain to work with thanks to different browser interpretations). The <a href="http://aloha-wikidocs.com/">Aloha collaborative wiki</a> seemed like a well thought out idea. Next time I'm looking for a WYSIWYG editor, I'll definitely
consider it.</p>
<h2>Paul Irish</h2>
<p>Paul Irish's (<a href="http://www.twitter.com/paul_irish">@paul_irish</a>) talk was also awesome, and I again took very few notes as I was busy being attentive; but Paul was explaining to the audience the web development stack and how it could help your development. Again, this was an excellent talk for the audience as half the audience didn't seem to be aware of most of these front end tools.</p>
<p>There were too many tools for me to list here (and if you keep up with the
web, you've probably heard of or used most of them) - but they are all listed on [Paul's slides](http://dl.dropbox.com/u/39519/talks/jquk-
tooling%2Bappstack/index.html). Paul's slides were using CSS3 transitions and animations to create 3D carousels, it was all very impressive and a quick check of the code shows it was <a href="https://github.com/hakimel/reveal.js">using
Reveal.js</a>. (thanks
<a href="http://www.twitter.com/autarc">@Autarc</a>)</p>
<p>One tool I wasn't aware of was the <a href="http://htmlemailboilerplate.com/">HTML Email
Boilerplate</a>. I don't have to build HTML
emails anymore (thankfully), but it's something I want to look in to as it
could be useful at work.</p>
<div class="post-image">
<img alt="Paul&#x27;s Talk" src="paul-small.jpg" srcset="paul-small.jpg 640w, paul-medium.jpg 1024w, paul-large.jpg">
</div>
<p>Paul wrapped up his talk with some demonstrations of upcoming Chrome Developer Tools functionality. As he was running short on time, he went quite quickly and I got the feeling there were some participants that weren't aware you could do <a href="/blog/2011/11/25-dev-tool-secrets/">most of the older functionality</a> that he was whizzing through to get to the new stuff.</p>
<p>First up was the ability to search all JavaScript files to find a particular piece of code with the search returning a list of all the results. Next (using a webcam so we could see the phone), he showed how he was connecting to Chrome on his Android and selecting elements on the phone through the Chrome Developer tools. It was very impressive to show how it was working, and he finished it with a cheeky wink.</p>
<p>[Paul's slides are available online](http://dl.dropbox.com/u/39519/talks/jquk-
tooling%2Bappstack/index.html).</p>
<h2>Addy Osmani</h2>
<p>Addy's (<a href="http://www.twitter.com/addyosmani">@addyosmani</a>) talk was quite
technical, probably a bit too technical for the some of the attendees who came from a design based background. But for the techies, it was very informative.</p>
<p>Addy spoke about how developers should break down large chunks of code in to smaller, reusable pieces. By separating units of code so that they do not relyvon one another (decoupling) you reduce the risk of parts of your code failingvand causing catastrophic errors.</p>
<h3>Publish and Subscribe</h3>
<p>By using publish and subscribe, you can allow modules of code to listen for
events and react to them accordingly (known as "Event Driven"). In jQuery,
this can already be done with <code>.trigger()</code> and <code>.on()</code>.</p>
<p>To listen for a custom event, you would use:</p>
<pre><code class="language-javascript">$(element).on("customEvent", myFunction);
</code></pre>
<p>To trigger that custom event, you would use:</p>
<pre><code class="language-javascript">$(element).trigger("customEvent");
</code></pre>
<p>There are a few alternatives to using these jQuery methods which are detailed on his slides.</p>
<h3>Patterns</h3>
<p>Next, Addy spoke about two patterns - the Facade pattern, which hides complex functionality behind a simple API; and the Mediator pattern, which is like Air Traffic control where a module handles all the communication between other modules.</p>
<h3>Modules</h3>
<p>At the lowest level, a solution to modules could be Object Literals. At a
higher level, modules could be created using the Module Pattern where privacy can be simulated (<a href="/blogs/2011/10/self-executing-anonymous-revealing-module-pattern/">I've written about this before</a>). At an even higher level, modules can be implemented with AMD - which is something I need to look in to
in more detail (perhaps a future blog post?).</p>
<div class="post-image">
<img alt="What are we looking for?" src="addy-small.jpg" srcset="addy-small.jpg 640w, addy-medium.jpg 1024w, addy-large.jpg">
</div>
<p>Addy had explained a lot of concepts, so it was time to merge them together. To build a large scale app, Addy was proposing that we separate our code into:</p>
<ol>
<li><strong>The Application Core</strong> - Using the Mediator pattern</li>
<li><strong>The Sandbox</strong> - Using the Facade patten</li>
<li><strong>Modules</strong> - Using one of the module solutions</li>
</ol>
<p>At the end of his talk, Addy showed us an impressive demo (another to-do list) where he swapped jQuery for Dojo and his app just carried on working.</p>
<p>[Addy's slides are available online](http://speakerdeck.com/u/addyosmani/p
/building-large-scale-applications-with-javascript-and-jquery).</p>
<h2>Doug Neiner</h2>
<p>Doug (<a href="http://www.twitter.com/dougneiner">@dougneiner</a>) is an excellent
speaker and really got across his point clearly. Having just had a rather
technical talk from Addy, Doug's talk was more related to building better
jQuery code and felt like it probably should have occurred earlier in the day as it gave some context to the content the other speakers spoke about.</p>
<p>Doug spoke about how you can improve your code by not initialising everything at the start of page load. Use Modernizr to check whether functionality is even supported (what's the point of implementing something that isn't even going to work?); and use tricks like measuring how far the user has scrolled before implementing functionality that is based far down the page (JIT initialisation).</p>
<p>Doug also spoke about making code less brittle by using jQuery functionality such as <code>.prevAll()</code> rather than <code>.prev()</code>; and <code>.closest()</code> instead of <code>.parent()</code>. Whilst these functions may be slightly less performant, in the majority of cases the performance hit is so minor and unimportant it doesn't actually matter. If another developer changes your HTML, your application isn't going to break.</p>
<p>His talk had a couple of excellent quotes:</p>
<blockquote>
<p>Write code like you spend money!</p>
</blockquote>
<p>Don't invest everything in to your code at the start, get something up and
running - and then invest in it later on if the code proves useful.</p>
<blockquote>
<p>Write code like you buy a car!</p>
</blockquote>
<p>While we'd all like a fast car, if we have a car that gets us from A to B that is normally sufficient. Faster cars whilst impressive aren't worth the cost. So weigh the difference between quality and cost.</p>
<p><a href="http://www.bit.ly/contextual-uk">Doug's slides are available online</a>. Sadly, there was no to-do list.</p>
<h2>In Summary</h2>
<p>If today was an episode of Sesame Street, the two themes would be to-do lists and how much the Android default browser sucks. But jest aside, it was an awesome day (with an awesome after party) - and all the speakers, and the guys at White October should be very proud. If you want to read more, check out the slides (links throughout) or <a href="https://github.com/jackfranklin/jQuery-UK-2012-Conference-Notes">Jack Franklin's
notes</a>.</p>
<p>Can't wait for the next one!</p>]]></content:encoded>
      <pubDate>Sat, 11 Feb 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Modernizr Prefixed</title>
      <link>https://www.andismith.com/blogs/2012/02/modernizr-prefixed</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/02/modernizr-prefixed</guid>
      <description>There was a new release of [Modernizr yesterday (2.5.1), and included within this release was some brand new features to the Modernizr Prefixed() API (introduced in 2.0), which can take away some of the pain of vendor prefixes from your JavaScript.</description>
      <content:encoded><![CDATA[<p>There was a new release of <a href="http://www.modernizr.com/download/">Modernizr yesterday
(2.5.1)</a>, and included within this release was some brand new features to the Modernizr Prefixed() API (introduced in 2.0), which can take away some of the pain of vendor prefixes from your JavaScript.</p>
<p>As you are probably aware if you are working with the latest JavaScript APIs and CSS3 technologies available in the browser, these are normally prefixed with a browser vendor tag. The reason for this is usually due to the feature still being in the experimental stage. As a simple example, if we wanted to scale an element on our page (and make it work cross-browser) we would have to write the following CSS:</p>
<pre><code class="language-css">#myElement {
  -moz-transform: scale(2); /* Firefox */
  -ms-transform: scale(2); /* IE (9+) */
  -o-transform: scale(2); /* Opera */
  -webkit-transform: scale(2); /* Safari and Chrome */
  transform: scale(
    2
  ); /* One day someone will use this so let's keep it future proof */
}
</code></pre>
<p>It's a lot of code to scale an element, and there are various arguments on the web about why vendor prefixes are and are not bad; and there are even some solutions to avoid having to write this amount of CSS, such as <a href="http://leaverou.github.com/prefixfree/">Lea Verou's -prefix-free</a> or [using a class file in LESS](http://net.tutsplus.com/tutorials/html-css-techniques/quick-tip-
never-type-a-vendor-prefix-again/).</p>
<p>But there are other problems which cannot be resolved so easily, such as
vendor prefixes in JavaScript. These are used for the same reason as in CSS, but there are some implementation differences - these prefixes are not
hyphenated and they are case sensitive.</p>
<pre><code class="language-javascript">$("#myElement").on("click", function () {
  var transform = "scale(2)";

  this.style.MozTransform = transform; /* Firefox */
  this.style.msTransform = transform; /* IE (9+) - note ms is lowercase */
  this.style.OTransform = transform; /* Opera */
  this.style.WebkitTransform = transform; /* Safari and Chrome */
  this.style.transform = transform; /* One day, my pretty */
});
</code></pre>
<p>But that's a lot of code.. It makes checking for a particular event or piece of JavaScript functionality a total pain - and this is where Modernizr
Prefixed comes in.</p>
<h2>Modernizr Prefixed to the Rescue</h2>
<div class="post-image">
<img alt="Modernizr" src="modernizr.jpg">
</div>
<p><code>Modernizr.prefixed(str)</code></p>
<p>At its simplest, Modernizr Prefixed allows us to find the relevant prefix for
the browser the user is currently visiting your page on - which is the only
browser we need to care about at runtime. For example, to find the relevant
prefix for transform we would use:</p>
<pre><code class="language-javascript">Modernizr.prefixed("transform");
</code></pre>
<p>The value returned in this instance is a string that is relevant to your
browser, as per the list above. Therefore, if we include Modernizr 2.5 in our
code we can use:</p>
<pre><code class="language-javascript">$("#myElement").click(function () {
  var transform = "scale(2)";
  this.style[Modernizr.prefixed("transform")] = transform;
});
</code></pre>
<p>As you can see, the saving is immediately apparent. Five lines of code for
vendor prefixes has become one. When a particular browser stops uses the
prefixing, Modernizr Prefixer will automatically pass the correct non-prefixed
value.</p>
<p><strong>Confused about the square brackets?</strong> In JavaScript, dot notation and square bracket notation are interchangeable - both are used to access properties of objects. In our example, we are accessing the style property object of our element, and in turn the transform property from our style. Dot notation is tidier to write and easier to read, but does not allow us to use variables to select properties. Using square brackets we can use the result of Modernizr.prefixed('transform') to select the property we want.</p>
<h2>But There's More..</h2>
<p>Modernizr Prefixed can accept multiple parameters, giving it extra uses.</p>
<p><code>Modernizr.prefixed(str, obj[, scope])</code></p>
<p>If you wanted to not only find out the prefixed value but also check whether
your prefixed property existed on a particular object, you could use:</p>
<pre><code class="language-javascript">$("#myElement").click(function () {
  var transform = "scale(2)";
  this.style[Modernizr.prefixed("transform", this.style, false)] = transform;
});
</code></pre>
<p>This code tells Modernizr to look for a transform property within
<code>this.style</code>'s properties. This example should always return a string
containing the vendor prefixed name, but testing other available functionality may return objects or functions; so to be safe we can ensure it does by supplying <code>false</code> as the third property. For example,
<code>Modernizr.prefixed('requestAnimationFrame', window)</code> would return a function (as requestAnimationFrame is a function, not a property).</p>
<h2>Binding functions to other functions</h2>
<p>By returning a function, we can simply other common uses of vendor prefixes.
For example, [Paul Irish's shim for
requestAnimationFrame](http://paulirish.com/2011/requestanimationframe-for-
smart-animating/) (an improved way of animating elements or a canvas on a page at a regular interval) previously looked like this:</p>
<pre><code class="language-javascript">window.requestAnimFrame = (function () {
  return (
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function (callback) {
      window.setTimeout(callback, 1000 / 60);
    }
  );
})();
</code></pre>
<p>But now can be written as follows:</p>
<pre><code class="language-javascript">window.requestAnimFrame =
  Modernizr.prefixed("requestAnimationFrame", window) ||
  function (callback) {
    window.setTimeout(callback, 1000 / 60);
  };
</code></pre>
<p>Modernizr Prefixed again handles all the vendor prefix nastiness for us -
meaning we just have to worry about the <code>timeout</code> condition for browsers that
do not support requestAnimationFrame at all.</p>
<h2>Vendor Prefix Appendix</h2>
<p>If you're confused about which vendor prefix is used on each browser (and
their casing), the below table is available to help you:</p>
<table>
<tbody><tr>
<th>Browser</th>
<th>CSS prefix</th>
<th>CSS prefix example</th>
<th>JavaScript prefix</th>
<th>JavaScript prefix example</th>
</tr>
<tr>
<td>Mozilla Firefox</td>
<td>-moz-</td>
<td>-moz-transform</td>
<td>Moz for properties and moz for functions*</td>
<td>MozTransform</td>
</tr>
<tr>
<td>Microsoft Internet Explorer</td>
<td>-ms-</td>
<td>-ms-transform</td>
<td>ms</td>
<td>msTransform</td>
</tr>
<tr>
<td>Opera</td>
<td>-o-</td>
<td>-o-transform</td>
<td>O for properties and o for functions*</td>
<td>OTransform</td>
</tr>
<tr>
<td>Google Chrome</td>
<td>-webkit-</td>
<td>-webkit-transform</td>
<td>Webkit</td>
<td>WebkitTransform or webkitTransform</td>
</tr>
<tr>
<td>Apple Safari</td>
<td>-webkit-</td>
<td>-webkit-transform</td>
<td>Webkit</td>
<td>WebkitTransform or webkitTransform</td>
</tr>
</tbody></table>
<p>You can read more about Modernizr Prefixed <a href="http://www.modernizr.com/docs/#prefixed">in the official
documentation.</a></p>]]></content:encoded>
      <pubDate>Sat, 11 Feb 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Keeping It Real Simple</title>
      <link>https://www.andismith.com/blogs/2012/02/keeping-it-real-simple</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/02/keeping-it-real-simple</guid>
      <description>Last week I had the opportunity to go on a presentation skills course, and one of the tasks we were set was to explain one aspect of our job role to a group of people who had no experience in our field using the LIONS approach using zero jargon.</description>
      <content:encoded><![CDATA[<p>Last week I had the opportunity to go on a presentation skills course, and one
of the tasks we were set was to explain one aspect of our job role to a group
of people who had no experience in our field using the LIONS approach using
zero jargon. LIONS stands for:</p>
<ul>
<li><strong>L</strong>anguage easily understood</li>
<li><strong>I</strong>llustrated</li>
<li><strong>O</strong>rganised Thoughts</li>
<li><strong>N</strong>arrow Subject</li>
<li><strong>S</strong>ummary</li>
</ul>
<p>We had 3 minutes to think of an idea and prepare, and 2 minutes to present. I
chose to speak about how my role as "Presentation Technical Architect"
involves ensuring the web standards model is enforced. That is, the separation
of structure, presentation and logic - otherwise known as HTML, CSS and
JavaScript, except I couldn't call them that at any point. It's a very basic
part of my job, but seemed a useful target for this demo. I decided to liken
my representation to that of a smiley face to keep it simple.</p>
<ul>
<li><strong>Structure</strong> - On a smiley, the structure would be the definition of the face and it's facial features. So, the number of eyes; whether there was a nose and whether there was a mouth. Each of these items belong in the face area.</li>
<li><strong>Presentation</strong> - On a smiley, the presentation would be the colour of the smiley; the shape of the face; the shape of the mouth (smiley, sad .etc).</li>
<li><strong>Logic</strong> - On a smiley face, the logic would be any movements the face can make - e.g. moving the mouth, or eyes - potentially due to some other factor (e.g something to look at).</li>
</ul>
<p>Okay, most smileys don't move - but the analogy made everyone in the room
understand the difference between these layers of a front end website. At the
time, I had to illustrate on the board each of these layers. Next time you
find yourself talking to someone who just doesn't get it, feel free to use
this example.</p>
<p>If you've come here from Google and you're just getting in to developing for
the web, the web standards model is something you will want to read more
about, so check out [the W3C's introduction to the web standards
model](http://www.w3.org/community/webed/wiki/The_web_standards_model_-
_HTML_CSS_and_JavaScript).</p>]]></content:encoded>
      <pubDate>Mon, 06 Feb 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Impress at Presentations with Impress.js</title>
      <link>https://www.andismith.com/blogs/2012/01/impress-with-impress</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2012/01/impress-with-impress</guid>
      <description>The other day I mentioned on Twitter that I was playing around with Bartek Szopka&apos;s Impress.js library to write a presentation and it seemed to gather some interest. I then gave the presentation, and that gathered some more interest; so I thought I would write a short blog post about how to use Impress.js.</description>
      <content:encoded><![CDATA[<p>(UPDATED for Impress.js v0.3)</p>
<p>The other day I mentioned on Twitter that I was playing around with <a href="http://bartaz.github.com/impress.js/#/bored">Bartek
Szopka's Impress.js library</a> to
write a presentation and it seemed to gather some interest. I then gave the
presentation, and that gathered some more interest; so I thought I would write
a short blog post about how to use Impress.js.</p>
<p>For those of you who aren't aware - Impress.js is a web-based presentation
framework which utilises CSS3 transitions and transforms to produce some
impressive effects.</p>
<p><img src="https://www.andismith.com/wp-content/uploads/2012/01/intro.jpg" alt="Impress.js"></p>
<p>Whilst it's primarily use is for presentations, it can also be used for simple
web pages due to it's HTML/CSS/JavaScript roots. Employing a variety of
different CSS animations, your presentations or pages can be brought to life
through scrolling, rotating and scaling on a 3D plane. Impress.js works in the
latest Chrome and Safari, as well as Firefox 10+ and Internet Explorer 10+.</p>
<p>So how do you use it?</p>
<h2>Content, Content, Content</h2>
<p>The most important part of the process of producing any presentation is the
presentation itself. There is no point making a whizzy presentation if you
have nothing to say.</p>
<p>Your slides should still follow the usual important rules of presentations - a
block of text really isn't going to look any better with Impress.js than it is
in PowerPoint or Keynote. Instead, keep slides short and to the point so you
can expand on them when speaking.</p>
<h2>Layout</h2>
<p>Once your presentation has been decided, you are <em>almost</em> ready to start using
Impress.js. But first, you need to consider how you are going to layout your
presentation. Because your Impress.js presentation can move in all directions,
rotate in any direction and even move closer or further away from the user,
it's very easy to get in a total mess.</p>
<p>The best way to avoid this mess is to draw out what you want to achieve before
you start; and also to keep things simple. Like an overused star wipe, if you
change direction and/or rotate to a different angle on every slide your viewer
is going to start getting a headache; and so are you making it.</p>
<p>Here's the layout we're going to go for in this example.</p>
<p><img src="https://www.andismith.com/wp-content/uploads/2012/01/impress.jpg" alt="Our Design"></p>
<p>Now you've decided on your presentation and your layout, it's time to get
started with writing the actual code.</p>
<h2>Initial Setup</h2>
<p>The foundation of any Impress.js presentation is the same as the foundation of
any good web page. We start with the usual HTML base (a head, a title and a
body tag), and add a &#x3C;script> element to load in the Impress.js library and a
containing element such as a &#x3C;div> with the id "impress".</p>
<pre><code class="language-html">&#x26;lt;!DOCTYPE html> &#x26;lt;html> &#x26;lt;head> &#x26;lt;meta charset="utf-8" />
&#x26;lt;title>Presentation Name&#x26;lt;/title> &#x26;lt;/head> &#x26;lt;body> &#x26;lt;div
id="impress"> &#x26;lt;!-- our slides will go here --> &#x26;lt;/div> &#x26;lt;script
src="library/js/impress.js">&#x26;lt;/script> &#x26;lt;/body> &#x26;lt;/html>
</code></pre>
<p>If you're using v0.3 or above of Impress you will also need to include the
following code after you include the impress.js library:</p>
<pre><code class="language-html">&#x26;lt;script>impress();&#x26;lt;/script>
</code></pre>
<p>Slides should be placed within your "impress" container and will appear in the
order they appear in your code. Each slide is represented with the class
"step", but they can be created with any block-level element (e.g., &#x3C;div>,
&#x3C;section>, &#x3C;header>). You could use the &#x3C;div> element throughout, but I prefer
to create my presentations with the first slide as a header element,
subsequent slides as sections and the final slide as a footer as I feel it
adds more semantic value. For example:</p>
<pre><code class="language-html">&#x26;lt;div id="impress"> &#x26;lt;header class="step"> &#x26;lt;h1>Andi's
Presentation&#x26;lt;/h1> &#x26;lt;/header> &#x26;lt;section class="step"> &#x26;lt;p>Here's
something cool on slide 2&#x26;lt;/p> &#x26;lt;/section> &#x26;lt;section class="step">
&#x26;lt;p>Here's something also cool on slide 3&#x26;lt;/p> &#x26;lt;/section> &#x26;lt;footer>
&#x26;lt;p>Visit me at &#x26;lt;a href="http://www.andismith.com"
target="_blank">AndiSmith.com&#x26;lt;/a>&#x26;lt;/p> &#x26;lt;/footer> &#x26;lt;/div>
</code></pre>
<h2>Positioning our Slides</h2>
<p>Now we've created some steps, it's time to refer back to our sketch and
position our slides accordingly. Impress.js uses the HTML5 data-* attribute to
define the positions of our slides. There are a number of options available,
which we will use as we build our slideshow.</p>
<h3>Position Options</h3>
<p>Slides are positioned relative to the centre of the presentation (0,0), rather than in relation to the previous slide.</p>
<p>Attribute - Description - Default</p>
<p><code>data-x</code> - Position the centre of the slide on the x axis (horizontal) at the pixel coordinate specified. Use negative values to position to the left. - 0</p>
<p><code>data-y</code> - Position the centre of the slide on the y axis (vertical) at the pixel coordinate specified. Use negative values to position upwards. - 0</p>
<p><code>data-z</code> - Position the centre of the slide on the z axis (depth) at the pixel coordinate specified. Use negative values to position away from the screen. - 0</p>
<p>We will keep our first slide at the center of the page, and change our second slide to appear 500 pixels below. To do this, we use the following code:</p>
<pre><code class="language-html">&#x26;lt;section class="step" data-y="600"> &#x26;lt;p>Here's something cool on slide
2&#x26;lt;/p> &#x26;lt;/section>
</code></pre>
<p>Our first slide doesn't need any values as it will sit at the default
position. We only need to specify the data-y value for slide 2 as it is
staying at the same x coordinate.</p>
<h3>Rotate Options</h3>
<p>Attribute - Description - Default</p>
<p><code>data-rotate</code> or <code>data-rotate-z</code> - Rotate the slide on the z-axis by n degrees. The centre of the slide is the point where rotation occurs. Use negative values to rotate anticlockwise. - 0</p>
<p><code>data-rotate-x</code> - Rotate the slide on the x-axis by n degrees. The centre of the slide is the point where rotation occurs. Use negative values to rotate toward the user on the horizontal plane. - 0</p>
<p><code>data-rotate-y</code> - Rotate the slide on the y-axis by n degrees. The centre of the slide is the point where rotation occurs. Use negative values to rotate toward the user on the vertical plane. - 0</p>
<p>For our third slide we are going to make it appear from the left and rotate
180 degrees (so it is upside down) using the following code:</p>
<p>&#x3C;section class="step" data-x="1200" data-y="600" data-rotate="180">
&#x3C;p>Here's something also cool on slide 3&#x3C;/p>
&#x3C;/section></p>
<h3>Scale Options</h3>
<p>Attribute - Description - Default</p>
<p><code>data-scale</code> - Scale the slide to make it bigger (e.g. 4) or smaller (e.g. 0.5). - 1</p>
<p>Our footer isn't as important as the other slides, so we will shrink this
slide by changing the scale to 0.5 (and, of course, moving it so it doesn't
sit in the way of the previous slides).</p>
<pre><code class="language-html">&#x26;lt;footer class="step" data-x="1200" data-scale="0.5"> &#x26;lt;p>Visit me at &#x26;lt;a
href="http://www.andismith.com" target="_blank">AndiSmith.com&#x26;lt;/a>&#x26;lt;/p>
&#x26;lt;/footer>
</code></pre>
<h2>Styling our Slides</h2>
<p>Styling of slides still occurs in a CSS file, just as it would for a website.
As we are only able to support newer browsers for our whizzy effects, and you are likely to have control over what browser your presentation is viewed in (at least initially), you may want to consider whether you drop support for older versions of IE. See <a href="http://www.html5please.us">HTML5Please</a> for information of what extra functionality you could use.</p>
<p>For our slides, I have the following simple stylesheet:</p>
<pre><code class="language-css">body {
  background: #eee;
  font: 62.5% helvetica, arial, sans-serif;
  margin: 0;
  padding: 0;
}

header,
footer,
section {
  background: #fff;
  border: solid 1px #666;
  border-radius: 12px;
  display: block;
  height: 480px;
  width: 760px;
}

h1 {
  font-size: 8em;
  margin: 0 auto;
}

p {
  font-size: 4em;
  margin: 0 auto;
}
</code></pre>
<h2>Active Slides</h2>
<p>Sometimes you may find you can see parts of other slides on your screen, and you may wish to hide them - or you may wish to highlight the current slide when it's the one you are speaking about. Impress.js dynamically adds a class of "active" to your current slide so you can add styles where you need.</p>
<pre><code class="language-css">.step {
  opacity: 0.2;
}

.step.active {
  opacity: 1;
}
</code></pre>
<h2>Linking to a Slide</h2>
<p>If you wish to provide quick route to a particular slide in your deck, adding an ID attribute to the slide will provide a quick link. For example, a slide with an ID of "statistics" will be accessible by appending "#/statistics" to the end of the URL.</p>
<h2>The Overview Slide</h2>
<p>Creating an overview slide is a pretty useful way of showing the user an
entire presentation on one screen. Using this view, the user can navigate
quickly to the slide they want. If you've created a presentation, this would probably be your last slide for users later reading your deck. If you've created something else, it may be your first slide.</p>
<p>The overview slide is built as an empty slide, centered in your presentation with a large data-scale value. This transparent slide will cause Impress.js to pull its view of the page back toward the user giving them a complete overview. As we want the user to be able to see all the slides at this point, we set some additional CSS properties so that they are clearly visible (and give the users a prompt that they can click on them).</p>
<pre><code class="language-html">&#x26;lt;div id="overview" class="step" data-x="600" data-y="300"
data-scale="2">&#x26;lt;/div>
</code></pre>
<pre><code class="language-css">.step-overview .step {
  opacity: 1;
  cursor: pointer;
}
</code></pre>
<h2>The Fallback Message</h2>
<p>Creating a fallback message for browsers that do not support Impress.js'
functionality is also straightforward. First, we need to add a class of
"impress-not-supported" to our body element. When Impress.js loads the page, it will check if it is supported and remove this class if everything is ok. Next, we need to create a fallback message within our HTML that has a CSS display setting of none, unless it has a parent with a class of "impress-not-supported".</p>
<pre><code class="language-html">&#x26;lt;div id="impress" class="impress-not-supported"> &#x26;lt;div
class="fallback-message"> &#x26;lt;p>Your browser doesn't support the features
required by impress.js, so you are presented with a simplified version of this
presentation.&#x26;lt;/p> &#x26;lt;p>For the best experience please use the latest Chrome,
Safari, or Firefox 10.&#x26;lt;/p> &#x26;lt;/div>
</code></pre>
<pre><code class="language-css">/* set some styles to show the slideshow normally for browsers we cannot
support */
.impress-not-supported .step {
  margin: 20px auto;
  opacity: 1;
  position: relative;
}

.fallback-message {
  display: none;
  margin: 20px auto;
}

.impress-not-supported .fallback-message {
  display: block;
}
</code></pre>
<h2>Get Presenting!</h2>
<p>And that's all there is to it! Now you know you're presentation doesn't suck, all you need to work on is your speech skills… Impress.js can't help you there I'm afraid.</p>
<p>I've <a href="http://www.andismith.com/examples/blog/impress-with-impress/impress/">uploaded the barebones demo presentation for you to check
out</a>, or <a href="http://bartaz.github.com/impress.js/#/bored">check out Impress.js' much more impressive
presentation</a> - and then have a go yourself!</p>]]></content:encoded>
      <pubDate>Tue, 31 Jan 2012 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Writing for the Web</title>
      <link>https://www.andismith.com/blogs/2011/11/writing-for-the-web</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2011/11/writing-for-the-web</guid>
      <description>Today is Blue Beanie Day, an anniversary where developers change their avatars to wear blue beanies to show their support for web standards. Today is also the day a new web initiative has been launched called Move the Web Forward, with a simple goal of making it easy for developers to start contributing to the web platform.</description>
      <content:encoded><![CDATA[<p>Today is Blue Beanie Day, an anniversary where developers change their avatars to wear blue beanies to show their support for web standards. Today is also the day a new web initiative has been launched called "<a href="http://movethewebforward.org/">Move the Web Forward"</a>, with a simple goal of making it easy for developers to start contributing to the web platform.</p>
<p>Two of the "activities" listed within this document are "Explore Front End Features" and "Write". To me, these feel like they should be one and the same thing. I was someone who used to explore front end features but never write about them. But as you are probably aware, I have recently started writing about my experiences. And now I am here to convince you to do the same.</p>
<p>Writing for the web can be daunting. As I wrote my first blog posts one
thought kept popping up in my head:</p>
<blockquote>
<p>What if I write something and someone tells me I'm stupid?</p>
</blockquote>
<p>It's a completely silly thought to have, but it's one that kept crossing my mind. Could I get heckled?</p>
<p>In April, I spoke at my first conference. I'd set myself a goal to publicly speak at a conference after attending Full Frontal the November before. It was something that scared me, but a friend convinced me and persevered when I had doubts. I found myself at a conference with 300 people attending in a part of the world I had never ever been to. When I arrived, I was scared. I was petrified. But the important thing to remember was that people weren't attending this conference to see me fail, they were there to share and gain
knowledge. No one would have taken any enjoyment if I had screwed up, and no one would have heckled if I had said something considered particularly wrong.</p>
<p>And it's the same with writing on the web. No one will heckle you - people may correct you, they may ask questions, they may provide better answers.</p>
<p>Writing my post on "25 Secrets of the Browser Developer Tools" was an awesome experience for me.</p>
<p>Whilst writing the article I expanded my own knowledge of each of the browsers tools as I tried to figure out whether what I was describing really was the best way to do something and also how you could replicate the same functionality in each browser. I primarily use Chrome Developer Tools and Firebug for development, so using the other tools to discover those features rapidly increased my familiarity with them.</p>
<p>The feedback I received from writing the article was overwhelming. Twitter was filled with people saying that they were unaware of a particular feature, or that they had learnt so much. To be able to enhance so many people's day to day development practices filled me with a warm fuzz, it was so rewarding. I went on to run an internal presentation at work to ensure that my colleagues also had the knowledge to improve their use of the tools.</p>
<p>Even after writing the article I was still learning. I learnt so much from the comments that people left. There were so many people that commented with their own methods, or corrections to my methods. And I was happy to have corrections or enhancements to what I had said, it made writing the article incredibly worthwhile.</p>
<p>Finally, the best part of writing a blog post is you have something to refer back to. If for some reason you stop using a particularly technology or tool for some months, re-reading an old blog post can provide the best refresher course you could ever ask for.</p>
<p>There are so many "big name" developers out there who share their experiences and because they work at Google or Opera or Mozilla it can feel daunting to try and do the same. But it shouldn't feel that way - writing about your experiences is a sure-fire way to increase your knowledge rapidly and to give back to the community.</p>
<p>So if you are nervous about writing, take a subject you know with some
familiarity and explore it's extra hidden depths. Write about what you know, and what you find. It doesn't matter if someone else has written about it before - maybe you have a new take on the idea. And when you are done, feel free to add a comment to the end of this post so I can check out what you found..</p>]]></content:encoded>
      <pubDate>Wed, 30 Nov 2011 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>25 Secrets of the Browser Developer Tools</title>
      <link>https://www.andismith.com/blogs/2011/11/25-dev-tool-secrets</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2011/11/25-dev-tool-secrets</guid>
      <description>Over the last few years there has been one tool that has helped out every web developer more than any other – the browser developer tools.</description>
      <content:encoded><![CDATA[<p>Over the last few years there has been one tool that has helped out every web
developer more than any other – the browser developer tools. Working in
harmony with the web browser, the developer tools allows us to manipulate DOM
elements, CSS styles, JavaScript and other useful information from the same
window often in real time.</p>
<p>Historically developers have used Firefox's Firebug add-on to develop and
debug their websites, but more recently each browser has developed its own set
of tools and each comes with its own advantages and disadvantages. Nowadays it
seems hard to imagine ever building a website without one of these handy
tools, which are normally accessible by either pressing "F12″ in Windows or
"Cmd" ⌘, "Option" ⌥ and "I" on the Mac, or by right clicking on the page and
selecting “Inspect Element”.</p>
<div class="scroll">
<table>
<thead>
<tr>
<th>Browser</th>
<th>Development Toolset</th>
<th>Type</th>
<th>Documentation</th>
</tr>
</thead>
<tbody>
<tr>
<td>Chrome</td>
<td>Developer Tools</td>
<td>Integrated</td>
<td><a href="http://code.google.com/chrome/devtools/docs/overview.html" target="_blank">Documentation</a></td>
</tr>
<tr>
<td>Firefox</td>
<td>Firebug</td>
<td><a href="http://www.getfirebug.com" target="_blank">Add-on</a></td>
<td><a href="http://getfirebug.com/wiki/index.php/Main_Page" target="_blank">Documentation</a></td>
</tr>
<tr>
<td>Internet Explorer</td>
<td>Developer Toolbar</td>
<td>Integrated</td>
<td><a href="http://msdn.microsoft.com/en-us/library/gg589507(v=VS.85).aspx" target="_blank">Documentation</a></td>
</tr>
<tr>
<td>Opera</td>
<td>Dragonfly</td>
<td>Integrated</td>
<td><a href="http://www.opera.com/dragonfly/documentation/" target="_blank">Documentation</a></td>
</tr>
<tr>
<td>Safari</td>
<td>Developer Tools</td>
<td>Integrated (<a href="http://developer.apple.com/library/safari/#documentation/appleapplications/Conceptual/Safari_Developer_Guide/2SafariDeveloperTools/SafariDeveloperTools.html#//apple_ref/doc/uid/TP40007874-CH3-SW7" target="_blank">Default off</a>)</td>
<td><a href="http://developer.apple.com/library/safari/#documentation/appleapplications/Conceptual/Safari_Developer_Guide/2SafariDeveloperTools/SafariDeveloperTools.html" target="_blank">Overview</a></td>
</tr>
</tbody>
</table>
</div>
<p>But are you using the developer tools to their full potential? The biggest
positive about the developer tools is that they are incredibly easy to use,
but as a result developers often miss out on a large proportion of the
functionality provided. Inspired by [a video talk by Paul Irish and Pavel
Feldman](http://paulirish.com/2011/a-re-introduction-to-the-chrome-developer-
tools/), I've compiled a list of "secrets" of the developer console. I'm not
expecting every one of these to be unknown to you, but hopefully some of these
will help you to become an even better web developer.</p>
<p>If you have any more secrets, feel free to leave a comment at the end of the
article and I'll update the post once I have verified them. I'd also love to
know what developer console you use as your primary development tool, let me
know below!</p>
<h2>The "Console" Tab</h2>
<p>The heart of any developer tool is the "console" tab where you can output
debug and execute commands in to the current web page.</p>
<h3>Referencing the current element</h3>
<p><strong>Chrome, Firefox, Opera, Safari</strong> - If you have an element currently selected in your "Elements" tab, you can use the reference $0 to call it within your code. For example, to see the elements' contents you would type $0.innerHTML. In Chrome and Safari, you can call the console from any of the other tabs by pressing "Escape" so you don't have to keep switching. In Firebug, the console is available through an icon to the left of the tabs or by pressing either Ctrl, Shift and L on Windows or Cmd ⌘, Shift and L on Mac.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/using-0.png" alt="Using $0 in the console"></p>
<p>In Opera, you can select the previous element you had highlighted using $1. In
Chrome and Safari, you can select the previous elements you have highlighted
using $1 - $4.</p>
<h3>Using console.log to output multiple values and objects at the same time</h3>
<p><strong>All</strong> - We know <code>console.log()</code> is incredibly useful for outputting debug to the developer console, and preferred over alerts, but it can be irritating to output a string followed by an object if you're not aware of this feature of logging. Using <code>console.log('message:' + $('#message'))</code> will only tell you that your message is an object, and logging the object by its self can get confusing if the log is happening in the middle of a loop.</p>
<p><code>console.log()</code> actually accepts multiple parameters, so you can output both
the string and the object from the same command using <code>console.log('message:', $('#message'))</code>; or any other combination of JavaScript type you can think of.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/console-log-multi.png" alt="Using console.log"></p>
<p>You can use <code>console.warn()</code> for warning messages; <code>console.error()</code> for error
messaging and <code>console.info()</code> for information messages. You can also use
<code>console.assert()</code> to test expressions for true or false.</p>
<p>(Thanks to Masklinn for additional information)</p>
<h3>Re-using a JavaScript command</h3>
<p><strong>All</strong> - If you've typed a command in to the JavaScript console and you wish to re-run it, simply hit the up key to scroll through a list of previous commands you have called from the console.</p>
<h3>Persist</h3>
<p><strong>Chrome, Firefox</strong> - There's an obvious button for persisting console content in Firefox right above the console, but it's slightly more hidden in Chrome. Right click in the console to reveal a menu with "Preserve Log upon Navigation" as an option.</p>
<h3>Viewing the source of an object</h3>
<p><strong>Firefox</strong> - Firefox supports the <code>toSource()</code> method which means it's available in Firebug for printing out the contents of an object as a string in to the console.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/to-source.png" alt="The toSource() command in action"></p>
<h3>Changing frame</h3>
<p><strong>Firefox</strong> - Running JavaScript commands from the console command line is incredibly useful, but if you have an iframe to contend with it quickly becomes a problem. Luckily you can use the following command to focus on the frame in question and execute your commands against.</p>
<p><code>cd(window.frames['frameName']);</code></p>
<p><strong>Chrome</strong> - Chrome allows you to change frame in a different way. On a page which contains frames, you'll need to go to the "Console" tab and choose your frame from the dropdown at the bottom of the console.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/frame-select.png" alt="Selecting frame"></p>
<p><strong>Opera</strong> - Opera also has a dropdown for changing frame which is available from the "Console" and "Documents" tab. The dropdown will only appear on the "Console" tab if there are frames to select.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/opera-frame-select.png" alt="Frame select in Opera"></p>
<p>(Thanks to Paul Irish and Daniel Herzog for the additional information)</p>
<h3>Copying your code straight to the clipboard</h3>
<p><strong>Chrome, Firefox, Safari</strong> - Using the <code>copy()</code> command within the developer tools console, you can copy the contents of a command straight to the clipboard.</p>
<h3>Making calculations in the browser</h3>
<p><strong>All</strong> - This tip makes a lot of sense, but it's surprising how many people don't use it! If you need to know the answer to a math calculation quickly (e.g. the width of three columns in a 456px container) you don't need to open Calculator. Just type the math question in to the developer tools' console and it will return your answer. It's 152, by the way.</p>
<h2>The "Scripts" Tab</h2>
<p>Home to all the JavaScript running on the page, the script(s) tab contains a
dropdown to allow you to select the script you wish to debug.</p>
<h3>Dealing with minified JavaScript</h3>
<p><strong>Chrome, Internet Explorer, Safari</strong> - Placing breakpoints on JavaScript makes debugging much easier, but if your code has already made it to production then it's probably been minified. How can you debug minified code? Helpfully, some of the browsers have an option to un-minify your JavaScript.</p>
<p>In Chrome and Safari, simply select the 'Scripts' tab, find the relevant file
in the drop down menu and then press the "{ }" (pretty print) icon located in
the bottom panel.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/pretty-print.png" alt="The Pretty Print option"></p>
<p>In Internet Explorer 9, click the tool icon by the script selection drop down
to find the option to format the JavaScript.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/formatjs.png" alt="Formatting the JavaScript in IE9."></p>
<h3>Watching variables</h3>
<p><strong>All</strong> - A common tool with .NET development, 'watch' allows you to monitor a list of variables from one handy area, at the top of the right hand bar on the "scripts" tab. Watching a variable is really easy, just type in the name of the variable and 'watch' will keep its value up to date.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/watch.png" alt="Watching a variable"></p>
<h3>Editing and executing JavaScript on the fly</h3>
<p><strong>Chrome</strong> - Rather than editing in a separate editor and reloading the page, with Chrome you can edit directly in to the page. Simply double click where you would like to change the code, and type! Hit Ctrl/Cmd and S to save.</p>
<h3>Creating a breakpoint whenever a JavaScript error occurs</h3>
<p><strong>All</strong> - Simply click the pause icon in the Script tab of your developer console to pause the script when the first JavaScript error occurs. The line that has caused the error will be highlighted for you to review.</p>
<h3>Creating a script breakpoint based on the DOM changing</h3>
<p><strong>Chrome, Firefox</strong> - If you know your page is breaking when a certain part of the DOM changes, or you just want to find out what script is responsible for changing that element's attribute, Chrome and Firebug both allow you to set up a JavaScript breakpoint effectively allowing you to find the culprit in your code. Simply highlight the element you want to monitor and right click to select the conditions to break on.</p>
<p>(Thanks to Jason Wilson for the additional information)</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/break-on-dom-change.png" alt="Right click for the options to break on a DOM
change"></p>
<h2>The "Elements" tab</h2>
<p>Known as the "HTML" tab in Firefox and the "Documents" tab in Opera, the
"elements" tab displays the DOM in its current state. On Internet Explorer,
you will need to hit the 'refresh' button to see the current DOM.</p>
<h3>Getting the dimensions of a container the easy way</h3>
<p><strong>Chrome, Safari</strong> - I'm a big fan of using <code>overflow:auto</code> to contain floated elements, but it causes problems in older versions of Internet Explorer unless you specify an actual width (auto and 100% won't suffice). The dimensions can be found in the "Computed Style" which is handy but still a number of clicks. Using Chrome or Safari, you can easily view the dimensions of an element by hovering over it in the source code in the "Elements" tab, or alternatively using the magnifying glass in the bottom toolbar.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/dimensions.png" alt="Hovering over the element to get the dimensions"></p>
<p><strong>Firebug, Internet Explorer, Opera</strong> - You'll need to select the "Layout" tab in the right hand panel or scroll through the list of computed styles in the right hand sidebar.</p>
<p>(Thanks to Masklinn for the additional information)</p>
<h3>Expand view of all elements</h3>
<p><strong>Firefox, Opera</strong> - In Firebug, on the "HTML" tab pressing asterisk (*) on the num keypad expands all elements except scripts and stylesheets. Holding shift and pressing asterisk will also expand the scripts and stylesheet (link) elements.</p>
<p>In Dragonfly on Opera, there's a button in the "Documents" tab to do the same
task, as shown below.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/expand-dom.png" alt="The Expand DOM button in Dragonfly"></p>
<h3>Incrementing margin, padding, width, height, border - even color</h3>
<p><strong>All</strong> - If you have an element which you need to modify the margin, padding, width or height for, you can use the cursor keys to increment/decrement the size.</p>
<ul>
<li>Simply use the up and down cursor keys to increment/decrement by a unit of 1.</li>
<li>In Chrome, Firebug and Safari you can increment/decrement by a unit of 10 by holding the "Shift" key whilst pressing the up and down cursor keys.</li>
<li>In Chrome and Safari, you can increment/decrement by a unit of 0.1 hold the "Alt" key whilst pressing the up and down cursor keys.</li>
<li>In Chrome and Safari, you can also increment/decrement by a unit of 100 by holding down the "Shift" key whilst pressing Page-Up and Page-Down.</li>
</ul>
<p>These shortcuts are especially helpful when you are unsure of exactly what the
correct size should be. In Chrome, Safari and Opera you can also use the up
and down keys to increment and decrement color values.</p>
<p>(Thanks to mikkelrom for the additional information)</p>
<h3>Styling :active, :hover, :focus, :visited states</h3>
<p><strong>Chrome, Firefox, Opera</strong> - Styling CSS in the developer console is awesome, but becomes a little more tricky when testing other element states such as hover. Thankfully, there is a solution.</p>
<p>Chrome has a button built for this purpose. In the "Elements" tab's right hand
column look for the dotted element/cursor icon which allows you to try other
states.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/hover-focus.png" alt="Viewing the states in Chrome"></p>
<p>For Firebug, in the right hand column click the arrow menu beside the Style
tab and selected your desired state.</p>
<p>In Opera, it's the icon that looks like a list under "Styles".</p>
<h3>Rotate through color definition types</h3>
<p><strong>Chrome, Safari</strong> - Colors can be defined in multiple ways on a web page - by name, through a hexadecimal value (3 or 6 digits), as rgb or hsl (or their alpha transparent versions). You can rotate through these different definitions in Chrome or Safari by clicking the color square next to your color.</p>
<p>(Thanks to Masklinn for the additional information.)</p>
<h3>Color picker</h3>
<p><strong>Opera</strong> - In Opera, clicking the color square next to a color allows you to select a different color via a handy picker.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/colorpicker.jpg" alt="Opera&#x27;s color picker"></p>
<h2>The "Resources" tab</h2>
<p>The resources tab lists all the stylesheets, JavaScript, images used on your
page. This tab unfortunately doesn't exist in Firebug or Internet Explorer,
although some of its features are integrated in to other tabs.</p>
<h3>Saving your changes</h3>
<p><strong>Chrome, Internet Explorer, Safari</strong> - Making changes to the CSS or JavaScript is great, but it becomes a hassle to re-implement in to your source code once you are happy.</p>
<p>In Internet Explorer, on each tab a "save" icon provides save to file
functionality on a per file basis.</p>
<p>Meanwhile the "Resources" tab in Chrome and Safari has a handy feature that
stores all your changes as specific revisions enabling you to quickly go back
and forth through all your changes. Find the file where you've applied the
changes (which can be easily accessed by clicking the file name next to the
element you've changed), and a list of revisions will be provided. In Chrome,
right click on the filename to save a new version of your file. In Safari
you'll unfortunately have to copy and paste.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/version-control.png" alt="Version control in action"></p>
<h3>Cookies and Storage</h3>
<p><strong>Chrome, Opera, Safari</strong> - Also in the list of resources is a easily accessible list of different storage options together with any data stored for each option. Opera has a "Storage" tab which does the same thing.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/storage.png" alt="View the values in storage"></p>
<h2>The "Network" tab</h2>
<p>The network tab shows all the resources and files downloaded to load your
page. In most cases the developer tools need to be open for the network tab to
show any information, so you may need to refresh. The tab is known as "Net" in
Firefox. For Internet Explorer, it is only available in version 9 and later.</p>
<h3>Disabling browser cache</h3>
<p><strong>All</strong> - Each supported browser allows you to disable cache, but there is no consistency with how.</p>
<p>In Chrome, you'll find the option in the settings cog. In Firebug, you'll find
the option in the arrow alongside the "Net" tab header. In Internet Explorer,
the option is under "Cache" in the menu bar.</p>
<p>In Opera, to clear the cache click the Network tab, select the Network Options
secondary tab and choose the first option. In Safari, you can disable your
browser cache under the Develop menu in the menu bar.</p>
<p>To bring up the clear cache (and other data) dialog on any browser in Windows
press Ctrl, Shift and Delete.</p>
<p>(Thanks to Steven and karl for the additional information)</p>
<h3>Latency</h3>
<p><strong>All</strong> - In Chrome and Safari, the network tab allows you to see how long it takes a server to respond to a request. The faded color line for each resource indicates when the request was sent, and when a response was sent back. The filled color indicates when the resource was downloaded. In Chrome, you can hover over these lines to get a breakdown of where the time was spent.</p>
<p>In Opera, the same principles apply except the latency is measured by a grey
line as opposed to a faded line.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/latency.png" alt="Latency in Opera"></p>
<p>In Internet Explorer, latency is marked as yellow and hovering over the line
will give more information.</p>
<p>In Firebug, latency is marked in the color purple and labelled as 'Waiting'.
Hovering over one of these lines in Firebug also gives a detailed breakdown of
where the time was spent.</p>
<h3>DOMContentLoaded, load event fired</h3>
<p><strong>Chrome, Safari</strong> - The network tab for Chrome and Safari also reveals 2 additional pieces of information, DOMContentLoaded as a blue line and load event fired as a red line.</p>
<p><img src="https://www.andismith.com/assets/blogs/2011/11/25-dev-tool-secrets/domready.png" alt="Showing the DOMContentLoaded"></p>
<p>The DOMContentLoaded line indicates when the browser has finished parsing the
document (but other resources such as images and stylesheets may not be
downloaded), whilst the load event line indicates when these resources are
complete.</p>
<p>If the two events fire at the same time, the line will be purple.</p>
<p>(Thanks to Steven and Joey for the additional information)</p>
<h2>Other</h2>
<h3>Crashing</h3>
<p><strong>All</strong> - Sometimes I find my developer tools have crashed and won't respond to my mouse clicks. Rather than close the entire browser window and re-open, I often find that using the keyboard shortcuts to close and re-open the developer tools fixes the problem.</p>
<p>I hope these features and secrets have been useful to you, although your skill
level and experience will determine how many of these are new to you. I
deliberately haven't included profiling and remote debugging in this list, as
these are topics I want to cover in more detail in a later post. Please feel
free to leave feedback, corrections and your own tips below!</p>]]></content:encoded>
      <pubDate>Thu, 10 Nov 2011 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Using jQuery .on() and .off()</title>
      <link>https://www.andismith.com/blogs/2011/11/on-and-off</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2011/11/on-and-off</guid>
      <description>With the release of jQuery 1.7 on November 3rd came two new ways to attach event handlers - .on() and .off(). These two additions unify all types of (good) event handling in jQuery and will help you write tidier and more efficient code in the future.</description>
      <content:encoded><![CDATA[<p>With the release of <a href="http://code.jquery.com/jquery-1.7.min.js">jQuery 1.7</a> on November 3rd came two new ways to attach event handlers - <code>.on()</code> and
<code>.off()</code>.</p>
<p>Whilst possibly not the most exciting new additions to jQuery's
large utility belt, these two additions do unify all types of (good) event
handling in jQuery and will help you write tidier and more efficient code in the future.</p>
<p>In this article, I'll give you a short introduction to both these handlers,
and how you can move from your existing code of <code>.bind()</code>, <code>.live()</code> or
<code>.delegate()</code> to <code>.on()</code> and <code>.off()</code>. Like everything in jQuery, the official documentation for <code>.on()</code> and <code>.off()</code> is pretty awesome and your best starting point for further questions, so I've included links throughout. We'll also take a look at using <code>.one()</code> for single use event handling..</p>
<aside>
{{% md %}}
**What is event handling?** The majority of JavaScript code is triggered from an event. The page loads, an event fires. You press a key or click a button, an event fires. You move your mouse, an event fires. Event handling is telling the JavaScript what to do in these situations (how to handle the event that has been fired). [Visit Quirksmode to read more about event handling](http://www.quirksmode.org/js/introevents.html)
{{% /md %}}
</aside>
<h2>on()</h2>
<p><strong>Official documentation:</strong> <a href="http://api.jquery.com/on/">http://api.jquery.com/on/</a></p>
<p>The on() event handler is designed to replace both the <code>.bind()</code> and
<code>.delegate()</code> event handlers. It looks like this:</p>
<pre><code class="language-javascript">$(elements).on(events [, selector] [, data], handler);
</code></pre>
<p>Where:</p>
<ul>
<li><strong>events</strong> is the event type(s). It can be one or more space-separated event types, e.g. 'click' or 'click keydown'. It can include an optional namespaces, e.g. "click.myPlugin".</li>
<li><strong>selector</strong> specifies the decendants of the elements that trigger the event. It is optional, and if it is not included the event will always trigger when the element event occurs.</li>
<li><strong>data</strong> is any data you want to pass to the handler when the event is triggered. This is also optional, and if used is normally an object.</li>
<li><strong>handler</strong> is the function to execute when the event is triggered.</li>
</ul>
<p>You can also use an eventsMap to specify multiple events attached to one
element.</p>
<pre><code class="language-javascript">$(elements).on(eventsMap [, selector] [, data]);
</code></pre>
<p>For example:</p>
<pre><code class="language-javascript">$("#container a").on({
  click: function (e) {
    e.preventDefault();
    console.log("item anchor clicked");
  },
  mouseenter: function (e) {
    console.log("enter!");
  },
});
</code></pre>
<h3>Changing from .bind() to .on()</h3>
<p>If you are currently using <code>.bind()</code> in your code, you have the easiest
transition to <code>.on()</code>. Simply replacing <code>.bind()</code> with <code>.on()</code> will - in most cases - update your code.</p>
<pre><code class="language-javascript">// old way - .bind(events, handler);
$("#container a").bind("click", function (e) {
  e.preventDefault();
  console.log("item anchor clicked");
});

// new way (jQuery 1.7+) - on(events, handler);
$("#container a").on("click", function (e) {
  e.preventDefault();
  console.log("item anchor clicked");
});
</code></pre>
<p>Don't feel you have to create the handler function within <code>.on()</code> - a tider
way of coding the handler would be to call a separate function:</p>
<pre><code class="language-javascript">// handler function
function handleClick(e) {
  e.preventDefault();
  console.log("item anchor clicked");
}

$("#container a").on("click", handleClick);
</code></pre>
<p>This has the added benefit of allowing you to remove individual handlers later
on using <code>.off()</code>.</p>
<h3>Changing from .live() to .on()</h3>
<p>jQuery's <code>.live()</code> has been deprecated as of version 1.7. <code>.live()</code> was
originally used for attaching event handlers to elements that did not
currently exist in the DOM. This was useful functionality for pages that
dynamically generate or load content, but this method has been considered bad practice for some time now.</p>
<p>There are a number of problems with <code>.live()</code>, most are to do with poor
performance on large pages, a slower response or unexpected behaviour due to <code>.live()</code> events being attached to the <code>document</code> element. Unfortunately there are still a large number of developers using <code>.live()</code> in their code. If you're one of them, please stop. See "[Why .live() is a Bad Option](http://www.ultimatewebtips.com/why-jquery-live-is-a-bad-option-to-
use/)" for more information.</p>
<p>So what should you use instead for dynamically generated content? You should delegate your handlers using the new <code>.on()</code> handler, of course! Assuming you are adding dynamic content to a DIV element with ID "container", instead of using:</p>
<pre><code class="language-javascript">// do not use! - .live(events, handler)

$("#container a").live("click", function (event) {
  event.preventDefault();

  console.log("item anchor clicked");
});
</code></pre>
<p>You should use the following:</p>
<pre><code class="language-javascript">// new way (jQuery 1.7+) - .on(events, selector, handler)

$("#container").on("click", "a", function (event) {
  event.preventDefault();

  console.log("item anchor clicked");
});
</code></pre>
<p>This will attach your event to any anchors within the <code>div#container</code> element, reducing the scope of having to check the whole <code>document</code> element tree and increasing efficiency.</p>
<p>Please note - the narrower your selector matches are, the more efficient your code will be. Although this example uses an anchor, try to avoid attaching a handler to every anchor. It is better to give a class or id where possible.</p>
<h3>Changing from .delegate() to .on()</h3>
<p>If you were using <code>.delegate()</code>, your code will need minor re-organisation to work as <code>.on()</code>. With <code>.on()</code>, the parameters selector and eventType have switched positions.</p>
<pre><code class="language-javascript">// old way - .delegate(selector, events, handler);
$("#container").delegate("a", "click", function (event) {
  event.preventDefault();
  console.log("item anchor clicked");
});

// new way (jQuery 1.7+) - on(events, selector, handler);
$("#container").on("click", "a", function (event) {
  event.preventDefault();
  console.log("item anchor clicked");
});
</code></pre>
<p>This is the only change for converting from <code>.delegate()</code>.</p>
<h2>off()</h2>
<p><strong>Official documentation:</strong> <a href="http://api.jquery.com/off/">http://api.jquery.com/off/</a></p>
<p><code>.off()</code> is <code>.on()</code>'s evil twin undoing some (or all) of the good deeds
<code>.on()</code> has done. It's syntax looks like this:</p>
<pre><code class="language-javascript">$(elements).off( [ events ] [, selector] [, handler] );
</code></pre>
<p>With <code>.off()</code> all parameters are optional - using the parameters will allow
you to be more specific in what event handling you wish to turn off.
Specifying <code>$(elements).off()</code> will remove all event handlers from the
element.</p>
<h3>Moving from .unbind() to .off()</h3>
<p>Simply replacing <code>.unbind()</code> with <code>.off()</code> will - in most cases - update your code.</p>
<pre><code class="language-javascript">// old way - .bind(events);
$("#container a").unbind("click");

// new way (jQuery 1.7+) - off(events);
$("#container a").off("click");
</code></pre>
<p>If you wanted to remove a particular handler and you have created a separate handler function you can use:</p>
<pre><code class="language-javascript">$("#container a").off("click", handleClick);
</code></pre>
<p>Using this method, it's possible to remove some of the event handler's
functionality without removing all of it.</p>
<h3>Changing from .die() to .off()</h3>
<p>As you are no longer using <code>.live()</code> anymore, you should also no longer use
<code>.die()</code>.</p>
<pre><code class="language-javascript">// do not use! - .die(events)
$("#container a").die("click");

// new way (jQuery 1.7+) - .on(events, selector, handler)
$("#container").off("click", "a");
</code></pre>
<p>As with live(), die() has been deprecated. That's right, it's time for
<code>.die()</code> to die!!!</p>
<h3>Changing from .undelegate() to .off()</h3>
<p>As with <code>.on()</code>, the order of these parameters has changed for consistency.</p>
<pre><code class="language-javascript">// old way - .undelegate(selector, events);
$("#container a").undelegate("a", "click");

// new way (jQuery 1.7+) - off(events, selector);
$("#container a").off("click", "a");
</code></pre>
<h2>one()</h2>
<p><strong>Official documentation:</strong> <a href="http://api.jquery.com/one/">http://api.jquery.com/one/</a></p>
<p>It's entirely possible that you may want your event to fire only once.
jQuery's <code>one()</code> event handler attachment will handle attach the event for the first click and unattach it for subsequent clicks without you needing to write any additional code. Although <code>one()</code> has been included in jQuery for some time, I've rarely seen it used. In version 1.7 it has been updated to allow for delegation.</p>
<pre><code class="language-javascript">$(elements).one(events [, selector] [, data], handler);
</code></pre>
<p>The parameters are the same as for <code>.on()</code>.</p>
<p>Now you've read an introduction to <code>.on()</code> and <code>.off()</code>, it's time to start
using them in the real world. I hope this article has provided a good starter and encouraged you to investigate these new additions. If you need further information I recommend reading the official documentation, but I'm happy to answer any questions below.</p>
<p>Good luck!</p>]]></content:encoded>
      <pubDate>Thu, 10 Nov 2011 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Front End Code Etiquette</title>
      <link>https://www.andismith.com/blogs/2011/11/front-end-code-etiquette</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2011/11/front-end-code-etiquette</guid>
      <description>One of the most interesting parts of working as a web developer comes from making a decision on how to organise code depending on a particular project or problem. There are multiple ways to do the same thing, and there&apos;s not necessarily a wrong or a right answer for every situation.</description>
      <content:encoded><![CDATA[<p>One of the most interesting parts of working as a web developer comes from making a decision on how to organise code depending on a particular project or problem.</p>
<p>There are multiple ways to do the same thing, and there's not necessarily a wrong or a right answer for every situation.</p>
<p>Take namespacing for example. As my previous post showed, there are multiple ways to declare methods within your namespace and none of them are incorrect.
We all have opinions on how code should be structured and we all have our own ideas of what constitutes acceptable coding rules.</p>
<p>Unfortunately sometimes I encounter a problem that irritates me, and it
happens much more often than I would like. It can be summarised in the
following sentence:</p>
<blockquote>
<p>If you are working on someone else's code, respect their coding standard guidelines.</p>
</blockquote>
<p>It is so common to see code which has had contributions from multiple
developers use multiple coding styles. It makes untidy code look even messier and it reduces maintainability.</p>
<p>Sure, you may <em>prefer</em> to lay your code out in different way, but unless there is a good reason for changing it (and 99% of the time, there isn't) stick with what conventions are already there. Some particular examples:</p>
<ul>
<li>CSS classnames that change between conventions. For example, styles that are all in lowercase with hypens, except for one area where underscores and/or camelCase is used.</li>
<li>Creating a new namespace for code that belongs in the existing JavaScript namespace.</li>
<li>Adding images, CSS or JavaScript to a completely different folder to where the rest are stored - or in to the root.</li>
<li>CSS styles blindly added to the end of a stylesheet, breaking the perceived structure of the code - or styles added in to the reset styles area.</li>
<li>Indenting of CSS styles or brackets.</li>
<li>A extra CSS or JavaScript file added for reasons logic cannot understand.</li>
<li>And my personal favourite - finding a single extra function that should belong in a namespace sitting outside in the global scope.</li>
</ul>
<p>For me, there is nothing worse than seeing a project where everyone on the
team is using a particular standard, and someone else isn't. As far as I'm
concerned, this person has poor attention to detail, this person is a code
vandal. I like tidy, maintainable code. I shouldn't be able to view source on
your code and be able to tell it was written by multiple people.</p>
<p>So next time you work on an existing project, take the time to read through
the code layout that already exists and ask yourself "how can I integrate the
code I write seamlessly in to this project?".</p>
<p>Be considerate not Taz.</p>]]></content:encoded>
      <pubDate>Wed, 02 Nov 2011 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Self Executing Anonymous Revealing Module Pattern</title>
      <link>https://www.andismith.com/blogs/2011/10/self-executing-anonymous-revealing-module-pattern</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2011/10/self-executing-anonymous-revealing-module-pattern</guid>
      <description>I thought as a way to kick this blog off I would share this JavaScript pattern with you which I&apos;ve started using recently. I can&apos;t take any credit for this pattern - I discovered it on my Internet travels, so all thanks and kudos go to Elijah Manor on &apos;Enterprise jQuery&apos;.</description>
      <content:encoded><![CDATA[<p>I thought as a way to kick this blog off I would share this JavaScript pattern with you which I've started using recently. I can't take any credit for this pattern - I discovered it on my Internet travels, so all thanks and kudos go to [Elijah Manor on 'Enterprise jQuery'](http://enterprisejquery.com/2010/10
/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/).</p>
<p>If you're good JavaScript developer you should be placing your code in a namespace. A namespace stops collisions with other JavaScript files, reduces the use of the global scope and generally makes things tidier. Having variables in the global scope is considered bad practice as the variable could be overwritten or modified by any other JavaScript code anywhere. The most common way of placing your code in a namespace would be in an object literal like this:</p>
<pre><code class="language-javascript">var example = {
  publicVariable: "publicValue",
  publicMethod: function () {},
};
</code></pre>
<p>Here we have a variable called example (which becomes our namespace) and we are declaring all our variables and methods within this. If we wanted to know the value of publicVariable we would call <code>example.publicVariable</code>; if we wanted to execute publicMethod we would call <code>example.publicMethod()</code></p>
<aside>
{{% md %}}
**What is the difference between a method and a function?**
<p>In JavaScript, a method is code that belongs to an object and which is able to interface with other code and data within that object (object-orientated). A function is code that sits outside of an object and can be called from anywhere. In the case of the example above, <code>publicMethod()</code> is associated with the object 'example', so it is therefore a method. It is, however, just a naming convention. JavaScript does not have (or have a need for) a type 'method', just a type 'function' which can be associated to an object.
{{% /md %}}</p>
</aside>
<p>If you wanted to separate your private and public methods and create a closure (a mini environment which has access to the namespaces' local variables even when the function is returned), you'd probably do this:</p>
<pre><code class="language-javascript">var example = (function () {
  var privateVariable = "privateVariable";

  function privateMethod() {
    return 1;
  }

  return {
    publicVariable: "publicValue",
    publicMethod: function () {
      //return privateMethod();
    },
  };
})();
</code></pre>
<p>Here we have changed our namespace in to a anonymous self executing function. We have created private variables and functions which are only accessible within this namespace, and public variables and functions which are returned by the self executing function making them accessible by our entire codebase.
We can call <code>example.publicMethod()</code>, but we cannot call <code>privateMethod()</code>
outside of the namespace. If we were to uncomment the line of code within
publicMethod(), we would be able to execute privateMethod from within this
namespace.</p>
<p>The following code is also self executing but sets out our namespace with a
much tidier, modular feel. It's my new favourite way of organising my code and has quickly become my most used pattern. Obviously, you should always use the best pattern for the job - there are some good books to read about JavaScript patterns (such as 'Pro JavaScript Design Patterns' by Dustin Diaz and Ross Harmes).</p>
<pre><code class="language-javascript">(function (example, $, undefined) {
  var privateVariable = "privateValue";

  example.publicVariable = "publicValue";

  function privateMethod() {}

  example.publicMethod = function () {};
})((window.example = window.example || {}), jQuery);
</code></pre>
<p>Focussing first on our top and bottom lines, you can see our arguments in the function call, and the values for these arguments at the bottom when the function is executed. The first difference to be noted with this approach is that we now pass the namespace in to the function as a parameter. We check whether it already exists in the global namespace (window). If it does we pass the existing namespace so we can append to it. If it does not, we pass an empty object literal.</p>
<p>The second argument passes in our jQuery object, although it could pass in any favourite JavaScript library. As the code we are executing is in its own local scope, we can now use the $ variable safe in the knowledge that no-one else has declared <code>$</code> for use as anything else. We also include an argument of undefined and pass in no correlating value which [removes the threat of someone else redefining undefined](http://pt.withy.org/ptalk/archives/2005/06/
dont_assume_undefined_is_undefined.html).</p>
<p>Within the function itself you still get you get all the benefits of private
and public variables and functions but you don't need to place all your public functions and variables in to a return object literal. This means your private functions can refer to your public ones without needing to specify the entire namespace (just <code>example.publicMethod()</code>), and you do not need to remember to use object literal notation - which makes for tidier code and less chance of missing a comma or two.</p>
<p>Finally, it's easy to expand. You can expand your module with more functions
and variables just by including <code>window.example = window.example || {}</code></p>
<p>Let me know what you think below, and be sure to [read the original article by Elijah!](http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-
bad-javascript-habits-part-1/)</p>]]></content:encoded>
      <pubDate>Thu, 27 Oct 2011 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
    <item>
      <title>Welcome</title>
      <link>https://www.andismith.com/blogs/2011/10/welcome</link>
      <guid isPermaLink="true">https://www.andismith.com/blogs/2011/10/welcome</guid>
      <description>If you&apos;re reading this, you&apos;ve reached the first post of my blog. Depending on whether I&apos;ve written a few or many blog posts at this point, this may be an achievement that you&apos;ve reached this post, or it may have been a simple slide of the finger.</description>
      <content:encoded><![CDATA[<p>If you're reading this, you've reached the first post of my blog.</p>
<p>Depending on whether I've written a few or many blog posts at this point, this may be an achievement that you've reached this post, or it may have been a simple slide of the finger. Regardless - <a href="http://www.youtube.com/watch?v=QH2-TGUlwu4">congratulations, here is your
prize.</a></p>
<p>So as a lame secondary reward, I thought I'd give you some back story as to how I became a web developer.</p>
<p>As a kid I was obsessed with making lists. Lists of anything and everything. Our house didn't get our first PC until I was 15 (1997) and at that point people in my neighbourhood weren't on the Internet.</p>
<p>After attending a introduction to further education at Brighton University, I discovered that the CD ROM which contained all our reading materials also included this program called Netscape which could be used to display all the reading material. I liked the idea of being able to produce documents with more freewill than Microsoft Word and as the reading material code was open source (thank you web!), I could peak in and figure out how to build these HTML pages myself. And therefore I learnt HTML in my spare time by creating both a simple college site for my friends and a Red Dwarf fan site (space background, the usual). Not that I had the Internet, so it could never go live.</p>
<p>It wasn't until a year or two later where I actually got the Internet and was able to post my first web page online. Some months later I was online and started my own website using the trusty HTML table layout I’d learnt all by myself to distribute some crappy games I’d made.</p>
<p>Then in 2000 I met Theo Chakkapark.</p>
<p>Without Theo, who knows where I’d be. I was frustrated and uninspired by having to learn Java at university thanks to C++ being removed from the syllabus after I enrolled. And the university were trying to suggest Java applets were the future of the web which felt wrong against everything I had taught myself.</p>
<p>“Why not try using Server Side Includes?” asked Theo. “They’ll let you include common content on every page without having to repeat it in your code.”</p>
<p>I was confused. “But I can use frames, and they are better! Look, I can scroll the sidebar independently of the main site!”</p>
<p>Thank goodness Theo stuck with me. Not only did he convince me Server Side Includes were better than frames, but he encouraged me to switch to ASP giving me a simple blogging system which I could learn from. I did learn from it, and suddenly got very ambitious. So Theo gave me an opportunity to run my site on his network, where it eventually became an incredibly popular community with over 10,000 registered users. His introduction to a basic feature of classic ASP led on to me producing a indie games community site, with dynamic updates, commenting, membership, forums and skins. Unfortunately, the site got so
popular I was eventually asked to move to my own server as I was eating
bandwidth – but my hunger for learning thereafter never stopped.</p>
<p>I went on to build an e-commerce site for my dissertation; then started work learnt ASP.NET; and then CSS and so forth. Theo helped me get a step on the ladder by taking the time to teach me how to improve on something I was interested in.</p>
<p>Now we are reaching a time where HTML5 and CSS 3 are coming of age. I find myself becoming Theo and teaching other web developers the benefits of learning proper JavaScript and the latest techniques in modern technologies; and this is what this blog is for. To talk tech, and to help others.</p>
<p>Thanks Theo, wherever you are. I don’t think he realises quite what an impact he made...</p>]]></content:encoded>
      <pubDate>Fri, 21 Oct 2011 00:00:00 GMT</pubDate>
      <author>Andi Smith</author>
    </item>
  </channel>
</rss>