<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matthew Lucas</title>
    <description>The latest articles on DEV Community by Matthew Lucas (@notmattlucas).</description>
    <link>https://dev.to/notmattlucas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F745337%2Fb5140acc-3a38-459b-baf8-b7262f693582.jpeg</url>
      <title>DEV Community: Matthew Lucas</title>
      <link>https://dev.to/notmattlucas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/notmattlucas"/>
    <language>en</language>
    <item>
      <title>Habot - Your Friendly Habit Coach (Azure Hackathon)</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Tue, 08 Mar 2022 21:29:08 +0000</pubDate>
      <link>https://dev.to/notmattlucas/habot-your-friendly-habit-coach-azure-hackathon-26dd</link>
      <guid>https://dev.to/notmattlucas/habot-your-friendly-habit-coach-azure-hackathon-26dd</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;When this hackathon opened, I was ploughing through the excellent book &lt;a href="https://jamesclear.com/atomic-habits"&gt;Atomic Habits&lt;/a&gt; by James Clear.&lt;/p&gt;

&lt;p&gt;Good habits are key for us to build happy lives and, conversely, bad habits can ruin them. But good habits are hard to build and bad habits hard to break - seems like we need a helping hand.&lt;/p&gt;

&lt;p&gt;The purpose of this hackathon entry was to build a helpful chatbot to coach you through building or breaking habits. Habot (your friendly habit-bot) will keep you on track, monitor your progress and give you helpful advice when you begin to falter - all a little "tongue in cheek" of course.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://habot.azurewebsites.net/"&gt;https://habot.azurewebsites.net/&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Let's take a look at some of the key features of Habot:&lt;/p&gt;




&lt;h4&gt;
  
  
  1. Single Signon with Outlook Credentials
&lt;/h4&gt;

&lt;p&gt;Using your Outlook OAuth credentials you can easily sign up with Habot, and it'll greet you by name and with a smile.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EWPCNq1N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/lucas-matt/habot/blob/main/imgs/x0.gif%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EWPCNq1N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/lucas-matt/habot/blob/main/imgs/x0.gif%3Fraw%3Dtrue" alt="Login" width="880" height="604"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  2. Creating your first habit
&lt;/h4&gt;

&lt;p&gt;The first thing you'll be asked to do once you log in is to create a habit.&lt;/p&gt;

&lt;p&gt;Habot is intelligent, and can infer one of a number of habits from what you say. For example, it can understand that "I want to begin going to the gym" is the same as "I want to start exercising more".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CtT95Img--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o71nf3n2vfh62pxrp8f8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CtT95Img--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o71nf3n2vfh62pxrp8f8.png" alt="Create Habit" width="880" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once your habit is confirmed, you'll get a cheery dance until next time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mkg-Ig4x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ds0ohn8l293gyhtwyt8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mkg-Ig4x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ds0ohn8l293gyhtwyt8f.png" alt="Create Complete" width="880" height="565"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  3. Check-in and Progress
&lt;/h4&gt;

&lt;p&gt;Each day you'll need to check in with Habot (I had hoped to use push notifications here, but alas no spare time). When you check in, Habot will keep track of your metrics.&lt;/p&gt;

&lt;p&gt;Habot will also give you charts to more clearly demonstrate your progress. It also has emotions, so will feel a little sad if you aren't doing as well as hoped.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r4z_RgUa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/lucas-matt/habot/main/imgs/x2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r4z_RgUa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://raw.githubusercontent.com/lucas-matt/habot/main/imgs/x2.gif" alt="Check In" width="880" height="604"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  4. Getting Help
&lt;/h4&gt;

&lt;p&gt;If you're having a hard time keeping up with your habits, Habot has some advice for you depending on the problem that you're facing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--buxhB2Re--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fknlx7uma2jtx8j7dkx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--buxhB2Re--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fknlx7uma2jtx8j7dkx.png" alt="Need Help" width="880" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Habot will also show you inspirational videos to help you get back on track.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---hhaNoKy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/irl9wlhbi5elznw4u7i1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---hhaNoKy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/irl9wlhbi5elznw4u7i1.png" alt="Help Given" width="880" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Wacky Wildcards&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Code on GitHub
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lucas-matt"&gt;
        lucas-matt
      &lt;/a&gt; / &lt;a href="https://github.com/lucas-matt/habot"&gt;
        habot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
habot&lt;/h1&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lucas-matt/habot"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;The project has a number of parts to it, and is built with the following technologies:&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Azure Bot Framework&lt;/strong&gt;, to drive the chatbot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FIfpTJEv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcsbvtr2904ayz8yq6xo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FIfpTJEv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcsbvtr2904ayz8yq6xo.png" alt="Azure Bot Framework" width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Vue.js&lt;/strong&gt; application for the UI, with the web-chat control built in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VzhtQ9UT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0tj7o1wr18iturp8gfs1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VzhtQ9UT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0tj7o1wr18iturp8gfs1.png" alt="Vue" width="256" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Rust&lt;/strong&gt; with Rocket backend to collect metric information and build charts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qWUx07bv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93ykgikk2vojc8j4b8oj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qWUx07bv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93ykgikk2vojc8j4b8oj.png" alt="Rust" width="256" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure CosmosDB&lt;/strong&gt; (Mongo flavour) to store metric information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gQ4pZNZX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqfcb0j9q2gv0i6ujrfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gQ4pZNZX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqfcb0j9q2gv0i6ujrfu.png" alt="Cosmos" width="289" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azuretrialhack</category>
      <category>azure</category>
      <category>ai</category>
    </item>
    <item>
      <title>SignPost - A Geo-Social Network (Atlas Hackathon)</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Thu, 13 Jan 2022 21:01:38 +0000</pubDate>
      <link>https://dev.to/notmattlucas/signpost-a-geo-social-network-atlas-hackathon-ied</link>
      <guid>https://dev.to/notmattlucas/signpost-a-geo-social-network-atlas-hackathon-ied</guid>
      <description>&lt;p&gt;For those of us who have had the good sense and fortune (or possibly misfortune?) to play one of the Dark Souls series, we're intimately familiar with the mystical orange soapstone. This item allows players to drop messages anywhere within their world that will then appear to other players in their own worlds. These are used to hint, warn, amuse or (more often than not troll) other players as they journey through the treacherous land of Lordran.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--95tUlBwI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2aqidzw5kcth10r79s9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--95tUlBwI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2aqidzw5kcth10r79s9b.png" alt="SignPost" width="477" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I took inspiration from the idea of the orange soapstone - specifically being able to drop messages at any location for others to read - and came up with the idea of 'SignPost', a geo-social network. This is a platform that allows you to drop messages (signposts) anywhere you like and to read the messages other people have left, where you're located. This could be great for directions, sports (cross-country hiking), games, etc - but is mostly just a fun idea for a hackathon.&lt;/p&gt;

&lt;p&gt;SignPost is an Android app supported exclusively by the MongoDB Atlas platform, from user-authentication and login through to geo-querying and search. The app has four core features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signup and authentication using your Google credentials (via MongoDB Realm).&lt;/li&gt;
&lt;li&gt;Browsing a map marked with "signposts" - messages dropped by other users near your location.&lt;/li&gt;
&lt;li&gt;Dropping a signpost yourself.&lt;/li&gt;
&lt;li&gt;Searching for signposts across the world &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  1. A map filled with signposts
&lt;/h4&gt;

&lt;p&gt;Once logged in (via your Google credentials) you're presented with a map zoomed in on your current location. The app fetches any messages in your local area and plots them on the map for you to interact with. These appear as orange markers, and will display the message when touched.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/RDmtvayeyg0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Dropping your own signpost on the map
&lt;/h4&gt;

&lt;p&gt;If you have something to say you can easily post your own message on the map. Just tap the exact point at which you want to post it and type away.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/77qtHJAXCio"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Searching for signposts across the world
&lt;/h4&gt;

&lt;p&gt;You can search for messages across the whole world, thanks to the Atlas search integration. Simply click the search icon (top right) pick the message from your search results and your map will be immediately transported around the world.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/TD9oVm_x9vY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  How does it all hang together?
&lt;/h4&gt;

&lt;p&gt;The SignPost app is backed completely by the MongoDB Atlas platform, and uses a number of its features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Realm / Google Authentication&lt;/strong&gt; to seamlessly add google user signon and tracking to the application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MongoDB Atlas with Geospatial Indexes&lt;/strong&gt; to allow the plotting of messages with their latitude/longitude co-ordinates, and to query records within the locality of a point.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Atlas Search&lt;/strong&gt; to make it trivial to map the signpost messages into a free text search.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Realm Functions&lt;/strong&gt; to provide a serverless set of backend APIs for the three key functions of the app - reading, writing and searching for messages.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3fA66x16--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ejhty2yb4qps3t17civ.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3fA66x16--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ejhty2yb4qps3t17civ.jpg" alt="Architecture" width="880" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In summary, MongoDB Atlas was able to provide a complete back-end stack (with the exception of Google Maps) to develop a fully functional Android application, even though I'd never written a line of Android code before 😲&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Choose Your Own Adventure&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lucas-matt"&gt;
        lucas-matt
      &lt;/a&gt; / &lt;a href="https://github.com/lucas-matt/signpost"&gt;
        signpost
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.mongodb.com/manual/geospatial-queries/"&gt;Geospatial Queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mongodb.com/realm/authentication/google/"&gt;Google Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mongodb.com/manual/geospatial-queries/"&gt;Geospatial Queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.atlas.mongodb.com/atlas-search/"&gt;Atlas Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.mongodb.com/realm/functions/"&gt;Realm Functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>atlashackathon</category>
    </item>
    <item>
      <title>Open Tracing on Kubernetes —Get Your Traces for Free</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Thu, 30 Dec 2021 14:23:32 +0000</pubDate>
      <link>https://dev.to/notmattlucas/open-tracing-on-kubernetes-get-your-traces-for-free-1c5h</link>
      <guid>https://dev.to/notmattlucas/open-tracing-on-kubernetes-get-your-traces-for-free-1c5h</guid>
      <description>&lt;p&gt;Ever since I first mucked around with Istio — a smart service mesh that runs on top of k8s — I was fascinated by its auto-injection feature. Flick the on-switch and Istio gets sprinkled across your existing deployment, giving you fantastic service-mesh powers, without modifying, repackaging or redeploying any of your existing apps in any way.&lt;/p&gt;

&lt;p&gt;Demystifying the process a bit, Istio uses a feature of Kubernetes named &lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#mutatingadmissionwebhook" rel="noopener noreferrer"&gt;“Mutating Admission Webhooks”&lt;/a&gt;. These are a lot simpler than they may sound. On deployment of a resource, k8s will send any active webhooks a YAML representation of the action being performed. These services can edit the deployment however they require — adding volumes, tweaking environment variables, checking parameters, etc.&lt;/p&gt;

&lt;p&gt;Why care? — Because open tracing, that’s why!&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s OpenTracing?
&lt;/h2&gt;

&lt;p&gt;OpenTracing is an initiative to enable reusable, open source, vendor neutral instrumentation for distributed tracing. In fact OpenTracing itself has become part of a larger project, OpenTelemetry, but that’s for another time.&lt;/p&gt;

&lt;p&gt;What’s a trace? A single trace represents the vapour trail left behind by a request after it has hopped around the services in your application. It gives you details about any HTTP requests, database calls, or other spans you may have set up. It can be a useful tool for understanding the shape of your traffic and to spot / debug any bottlenecks across a suite of microservices.&lt;/p&gt;

&lt;p&gt;How does this concern webhooks? Well, there’s some small amount of development required to get tracing hooked up in the first place. As the value is somewhat limited unless you instrument all apps in your platform, if you have 10+ microservices this effort can add up quite quickly. Wouldn’t it be great if you could try before you buy by just flicking that switch as you can with Istio.&lt;/p&gt;

&lt;p&gt;The rest of the article explains just this feature — for Java apps at least — and how it all hangs together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Special agents
&lt;/h2&gt;

&lt;p&gt;Amongst the treasures found within the &lt;em&gt;opentracing-contrib&lt;/em&gt; project is a Java &lt;a href="https://github.com/opentracing-contrib/java-specialagent" rel="noopener noreferrer"&gt;special agent&lt;/a&gt;. By plugging this into our app via the &lt;em&gt;-javaagent&lt;/em&gt; JVM flag we can fully enable tracing across any commonly used 3rd party libraries without changing any code or rebuilding the project. The list of &lt;a href="https://github.com/opentracing-contrib/java-specialagent#44-instrumentation-plugin" rel="noopener noreferrer"&gt;instrumented libraries&lt;/a&gt; is pretty comprehensive — Jersey, Cassandra drivers, MongoDB drivers to name but a few.&lt;/p&gt;

&lt;p&gt;What we’ll do here is automatically plug-in this agent at deployment time using a combination of webhook, init container and tweaking of environment variables to insert the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto tracing webhook
&lt;/h2&gt;

&lt;p&gt;The full source is available &lt;a href="https://github.com/lucas-matt/auto-tracing-webhook/blob/master/webhook.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but at it’s simplest the webhook will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the incoming deployment descriptor for the correct tags (autotrace: enabled). If present it’ll apply steps 2 onward, otherwise it’ll leave everything untouched.&lt;/li&gt;
&lt;li&gt;Add a volume mount into which the opentracing special agent jar will be dropped.&lt;/li&gt;
&lt;li&gt;Add an init container to copy the jar into the shared mount before the application boots.&lt;/li&gt;
&lt;li&gt;Tweak the JAVA_TOOL_OPTIONS environment variable to add the javaagent line for the agent.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Deploying it all
&lt;/h2&gt;

&lt;p&gt;First make sure you have Jaeger running in your default namespace — as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/jaegertracing/jaeger-kubernetes/master/all-in-one/jaeger-all-in-one-template.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;[edit]&lt;/em&gt;&lt;br&gt;
&lt;em&gt;If you’re using Kubernetes 1.16+ the api changed enough to break the jaeger deployment descriptor above. If you hit an issue try this fixed version instead:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/jaegertracing/jaeger-kubernetes/cc2e03335d8fe88eeef46648cff39151215ca97f/all-in-one/jaeger-all-in-one-template.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, the webhook itself. The source for which is available here: &lt;a href="https://raw.githubusercontent.com/lucas-matt/auto-tracing-webhook/master/webhook.yml" rel="noopener noreferrer"&gt;webhook.yml&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/lucas-matt/auto-tracing-webhook/master/webhook.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly make sure you label the target namespace so that the webhook gets activated for any deployment within.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl label namespace default autotrace=enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As far as this stage goes, we’re done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it out
&lt;/h2&gt;

&lt;p&gt;Let’s give the solution a quick run through. We’ll need to demonstrate a request across multiple services to show the tracing working well, end-to-end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -n &amp;lt;your-namespace&amp;gt; -f https://raw.githubusercontent.com/lucas-matt/pass-the-buck/master/deployment.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/lucas-matt/pass-the-buck/master/deployment.yml" rel="noopener noreferrer"&gt;Deployment.yml&lt;/a&gt; creates a chain of services A, B and C. A calls B, B calls C and C calls upstream to the &lt;a href="http://worldclockapi.com/api/json/gmt/now" rel="noopener noreferrer"&gt;world clock API&lt;/a&gt;. Each of the services is, by default, a completely trace unaware Spring Boot application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkznw39smp2bnspxhvjcm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkznw39smp2bnspxhvjcm.jpeg" alt="Service Chain"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of these services is tagged with the _autotrace: enabled _ label so that our webhook knows to inject instrumentation into the application at deploy time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service-a&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service-a&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service-a&lt;/span&gt;
        &lt;span class="na"&gt;autotrace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;enabled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After port forwarding service-a, all that remains is to make that request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqqg09ssq1oltu9ub9ga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqqg09ssq1oltu9ub9ga.png" alt="cURL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far so good — we got the time — but the main question, was it traced? Time to check Jaeger:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fji242pnty8ulkk3mlr4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fji242pnty8ulkk3mlr4x.png" alt="Jaeger"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success! We can see how the request moves along each service and out onto the open web.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Not a solution I’d use in production in its current state — for one, the untuned startup of an app is degraded whilst the agent scans the classpath for the complete set of libraries into which it can slot itself — but an interesting experiment nonetheless.&lt;/p&gt;

&lt;p&gt;If you’re interested in checking out OpenTracing, or even in how to create your own Kubernetes webhook, please take a look at the source repositories for a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/lucas-matt/auto-tracing-webhook" rel="noopener noreferrer"&gt;https://github.com/lucas-matt/auto-tracing-webhook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lucas-matt/pass-the-buck" rel="noopener noreferrer"&gt;https://github.com/lucas-matt/pass-the-buck&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>monitoring</category>
      <category>devops</category>
      <category>cloud</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Why Small Teams Outperform the Competition</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Wed, 22 Dec 2021 15:49:10 +0000</pubDate>
      <link>https://dev.to/notmattlucas/why-small-teams-outperform-the-competition-32if</link>
      <guid>https://dev.to/notmattlucas/why-small-teams-outperform-the-competition-32if</guid>
      <description>&lt;p&gt;Two pizza teams, scrum teams, tiger teams; there are many names for these close- knit bands of high-achievers who speed past the competition to fame and fortune. And there is one factor common to any well-performing team that greatly contributes to that success - &lt;strong&gt;&lt;em&gt;size&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Surely bigger means better? More resource means more hands to do the heavy lifting and more feet to stomp out those blazing fires. Of course, any modern worker with even a little experience understands that this is clearly not the case; but why is that?&lt;/p&gt;

&lt;p&gt;First lets look at what principles make up a winning team.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes up a winning team?
&lt;/h2&gt;

&lt;p&gt;According to Professors Hirotaka Takeuchi and Ikujiro Nonaku, in their paper &lt;a href="http://www.google.com/url?q=http%3A%2F%2Fhbr.org%2F1986%2F01%2Fthe-new-new-product-development-game%2Far%2F1&amp;amp;sa=D&amp;amp;sntz=1&amp;amp;usg=AFQjCNGhzT_G7WGft3YJsCVhtj4rp2F47Q"&gt;The New New Product Development Game&lt;/a&gt;, great teams embody three core principles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transcendence&lt;/strong&gt;: a sense of purpose, or self-realised goal that surpasses the ordinary aims of an average team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autonomy&lt;/strong&gt;: having full control over their destiny by being empowered to make key decisions and stick to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-Functional&lt;/strong&gt;: having all skills necessary to see a project through from start to finish without relying on a third party.&lt;/p&gt;

&lt;h2&gt;
  
  
  What has this got to do with size … ?
&lt;/h2&gt;

&lt;p&gt;These principles can only work if the team is able to hold a shared vision and shared understanding of the state of the world.&lt;/p&gt;

&lt;p&gt;Every member of a team needs to know exactly what they’re fighting for and why that is important. Every team member needs to understand the key priorities facing them at any moment. And finally, there has to be a good amount of trust within the team so that discussion can be fair, open and free flowing.&lt;/p&gt;

&lt;p&gt;We are, of course, social animals and our ability to achieve great things is deeply rooted in our sociology and biology. But, as we shall see, there are limits to how far this can be pushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Brain power
&lt;/h2&gt;

&lt;p&gt;You’ve likely heard the assertion that an average adult can hold only 7 ± 2 chunks of information in their working memory at any one time; a chunk being any atomic idea within our heads. The amount of information contained in a chunk can vary depending on our expertise in a topic —for example, a phrase in your native language many only form one chunk, whilst that same phrase in an unfamiliar language could take up many as you struggle to juggle the grammar in your mind.&lt;/p&gt;

&lt;p&gt;This limited ability to pay attention to many things at once is part of our human nature, and it applies not just to generalised thinking but to all aspects of our lives, including &lt;strong&gt;&lt;em&gt;communication&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As we increase a team’s size, the number of lines of communication doesn’t increase linearly, as you may expect, but by the addition of a new channel between the new member and every other member of the team.&lt;/p&gt;

&lt;p&gt;By plotting the formula that describes this — &lt;strong&gt;n(n-1)/2&lt;/strong&gt; — you can see that things can get quite of hand very quickly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bxj6drqh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i5kd4jzwidrnmyaoi3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bxj6drqh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i5kd4jzwidrnmyaoi3p.png" alt="Communication Overhead" width="700" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each line of communication is another mental burden that your team members have to struggle with whilst trying to hold everyone else’s trajectory in mind. At some point, between the 5 and 9 mark, their brains just give up and teams tend to splinter into more cognitively manageable subgroups.&lt;/p&gt;

&lt;p&gt;It’s biological — large groups just get less done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Social proof
&lt;/h2&gt;

&lt;p&gt;Think back to one of those situations where your team has to make serious commitment by way of a delivery date or some similar pressure? I’m sure there has been more than one occasion where this has resulted in a lot of poker faced, nervous shuffling as no-one wants to be the first to step up and make a suggestion for fear of putting their reputation at risk, or simply making of fool of themselves. Once someone finally does crack and gives it a go I bet that most, if not all of the rest of the team has followed suit pretty closely with the same or a very similar opinion.&lt;/p&gt;

&lt;p&gt;This is an example of social proof.&lt;/p&gt;

&lt;p&gt;Let’s take a more commonly occurring, but definitely insidious, example … the abomination of canned laughter.&lt;/p&gt;

&lt;p&gt;Everyone hates canned laughter. It’s been polluting our comedy shows and TV screens (or rather speakers) for decades but continues to persist, and there is a good reason for this. It has been scientifically proven that we are easily nudged into a chuckle by hearing a number of other people rolling around the floor in hysterics, despite the fact we know they are phantom and have been cleverly added after the fact by some sound engineer.&lt;/p&gt;

&lt;p&gt;All of this comes down to a simple heuristic or biological short-cut — that given some uncertainty (lack of concentration, understanding, or similar), we will will make fewer mistakes by acting in accord with the existing social evidence. In other words, usually when a lot of people do something and we’re unsure, it is probably the right thing to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pluralistic ignorance
&lt;/h2&gt;

&lt;p&gt;Closely related to social proof, is the concept of pluralistic ignorance.&lt;/p&gt;

&lt;p&gt;Let’s take the example discussed in Cialdini’s masterpiece. An actor pretending to have a heart attack, calls out for help in a public place. You’d think that any passers-by would come rushing in to save his life, but you’d be wrong. Entire groups of people fail to come to his aid, each certain that someone else will surely have helped and the ambulance must already be on its way. With several potential helpers around, the personal responsibility of each individual is reduced. Again, the uncertainty of the situation causes others to “just do” what everyone is doing.&lt;/p&gt;

&lt;p&gt;We can translate this into a less extreme circumstance in the case of some important task that needs to be completed. Everyone agrees that the build system is a nightmare and needs to be fixed, or that waiting an hour for the tests to run is just an utter joke, but does anyone actually do anything — probably not.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what can we learn from this?
&lt;/h2&gt;

&lt;p&gt;Clearly a team is a very delicate ecosystem and individually we’re plagued by our cognitive biases too, but there are many strategies that we can employ to try and make the best of it all. Here are a few suggestions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Keep teams small (ideally 5–7 people) to avoid paying too high a mental cost just in communication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid anchoring or social bias problems by using tools such as &lt;strong&gt;&lt;em&gt;planning poker&lt;/em&gt;&lt;/strong&gt; or by avoiding asking for commitments in an open and pressured forum.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Assign actions, and agree on an owner for any kind of important task that may otherwise fall victim to pluralistic ignorance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.amazon.co.uk/Scrum-Doing-Twice-Work-Half/dp/1847941109/ref=sr_1_1?ie=UTF8&amp;amp;qid=1537120954&amp;amp;sr=8-1&amp;amp;keywords=scrum"&gt;Scrum: The Art of Doing Twice the Work in Half the Time by Jeff Sutherland&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.co.uk/Influence-Psychology-Robert-Cialdini-PhD/dp/006124189X/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1537121063&amp;amp;sr=1-1&amp;amp;keywords=persuation"&gt;Influence: The Psychology of Persuasion by Robert Cialdini PhD&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>career</category>
      <category>performance</category>
    </item>
    <item>
      <title>Survivorship Bias and Negotiating Tech Hype</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Sat, 18 Dec 2021 07:17:40 +0000</pubDate>
      <link>https://dev.to/notmattlucas/survivorship-bias-and-negotiating-tech-hype-6i8</link>
      <guid>https://dev.to/notmattlucas/survivorship-bias-and-negotiating-tech-hype-6i8</guid>
      <description>&lt;h2&gt;
  
  
  Bombers with bullet holes
&lt;/h2&gt;

&lt;p&gt;During the dark days of World War II the American military presented their Statistical Research Group with a problem. They wanted to add additional armour to their bombers, but clearly couldn’t put the armour everywhere because of the additional weight it would add to the planes. The group was tasked with working out how much armour to allocate to the various regions of the aircraft to maximise defence whilst minimising any effect on fuel consumption and agility.&lt;/p&gt;

&lt;p&gt;Engineers inspected a number of bombers that had seen some action. These planes had bullet holes that were distributed mainly across the wings and body. Comparatively the engines and cockpit had much less damage. This had lead the commanders to make the obvious, but foolish, conclusion that they should enhance armour on areas that had been hit most frequently, namely the fuselage and wings.&lt;/p&gt;

&lt;p&gt;One of the many geniuses of the group, Abraham Wald, realised that they were looking at the problem from completely the wrong angle. It’s not that planes weren’t being hit as frequently on the engines and cockpit, but rather those that had been never returned to tell the tale! These were the parts of the aeroplane that needed enhancement, not the areas that could take a battering and still survive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Survivorship bias
&lt;/h2&gt;

&lt;p&gt;Countless articles, books and documentaries have been produced about successful people and how to capture the principles of their success to improve your own fortune.&lt;/p&gt;

&lt;p&gt;Consider Steve Jobs — frequently heralded as a one of the greatest geniuses of our time — how do we emulate his success? Clearly dropping out of college, spending time at meditation retreats and starting a business from your parent’s garage is the way to go. But what about the hundreds of thousands of budding Apple founders for whom this strategy never quite worked out?&lt;/p&gt;

&lt;p&gt;Books aren’t usually written about failed enterprises, just the rare, billion-dollar, success stories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing tech thoughtfully
&lt;/h2&gt;

&lt;p&gt;As well as being responsible for the latest diet fads and the exaggerated performance of mutual funds, we can see this bias lurking in certain corners of the software development world.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddf2fxjv8cc34qi153eq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddf2fxjv8cc34qi153eq.jpeg" alt="Thinking Homer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How often do you see a wave of enthusiasm for the next high throughput NoSQL system or a push for complicated elastic scaling technology? Companies such as Twitter and Netflix present their wild successes but we don’t usually see qualifications on the size and scale of the teams implementing these solutions.&lt;/p&gt;

&lt;p&gt;It’s worth keeping in mind the potential for a mass of silent teams. Struggling under the weight of overpowered, over-engineered, “web-scale” technologies inspired by the industry front-runners. Most of us mere mortals just don’t have the resources, skills, or (most importantly) even the need for such high class deployments. Most of the time it’s just better to keep things simple and known.&lt;/p&gt;

&lt;p&gt;Similarly, businesses push on with “AI” for fear of missing out, but without any real understanding of what they really need. Solutions are commissioned that aspire to the heights of Facebook and Google whilst in reality they fumble for a real business-value providing use-case.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong, I’m 100% all for learning new paradigms, languages and frameworks. This is just a reminder, as much to myself as anyone else, to take a moment to think past the biases that may lead us to make some regrettable, albeit well-intentioned and over-excited, choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youarenotsosmart.com/2013/05/23/survivorship-bias/" rel="noopener noreferrer"&gt;You Are Not So Smart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/dp/071819604X" rel="noopener noreferrer"&gt;How Not to Be Wrong: The Hidden Maths of Everyday Life&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy" rel="noopener noreferrer"&gt;High performance envy/web scale envy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Tell Don’t Ask</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Sun, 12 Dec 2021 20:53:05 +0000</pubDate>
      <link>https://dev.to/notmattlucas/tell-dont-ask-3ilk</link>
      <guid>https://dev.to/notmattlucas/tell-dont-ask-3ilk</guid>
      <description>&lt;p&gt;Quality software, and most importantly software that maintains that quality, is written in a way that protects itself from future corruption. It makes it easy to build upon without introducing new bugs or breaking existing features.&lt;/p&gt;

&lt;p&gt;There are many strategies to help us achieve this quality — unit testing for one — but there is an aspect of object-oriented programming that is designed specifically to tackle this problem. The key is to keep data and the logic that uses that data close together.&lt;/p&gt;

&lt;p&gt;The aim is to produce “&lt;em&gt;shy&lt;/em&gt;” code. Code that doesn’t expose too much to anyone who interacts with it, and only interacts through well-defined commands and queries. This is the very definition of encapsulation. It gives us the protection we need to build in robustness and it is applicable at all levels — object, module and service.&lt;/p&gt;

&lt;p&gt;Unfortunately, we all slip up from time to time and accidentally reveal more than we should’ve. There is, however, a principle that can help us avoid this pitfall.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell Don’t Ask
&lt;/h2&gt;

&lt;p&gt;When it comes to combining logic with the data that drives it there are two ways you can approach this, and they closely align with procedural and object-oriented schools of thought.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asking
&lt;/h3&gt;

&lt;p&gt;Procedural code generally asks for some data and then acts on this data. The behavior and data are kept separate.&lt;/p&gt;

&lt;p&gt;Take the following example of a bank account. The account has a fixed overdraft limit and a set of transactions (tuples of payee x amount).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overdraft&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overdraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overdraft&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Say we want to calculate and print the balance of the bank account — a function &lt;em&gt;print_balance&lt;/em&gt;. In the procedural style, the function has to pull everything it needs from the bank account and handle all calculations itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;amt&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current Balance: %s"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overdraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(Overdrawn by %s)"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overdraft&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whilst this gives a lot of power to the caller it also hands over much of the responsibility. Due to the loss of encapsulation, we’re now exposed to duplication, coupling and inevitably bugs.&lt;/p&gt;

&lt;p&gt;This is an anti-pattern known as the &lt;a href="https://martinfowler.com/bliki/AnemicDomainModel.html"&gt;Anemic Domain Model&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Telling / Querying
&lt;/h3&gt;

&lt;p&gt;Rather than asking the data structure for all of its data, if we tell (or query) the object for that information, the responsibilities become clearer and the data model becomes richer.&lt;/p&gt;

&lt;p&gt;Instead of asking for the raw data, we ask for facts for which the object is responsible — the account balance and available credit.&lt;/p&gt;

&lt;p&gt;Rather than adding any transactions directly, we inform the account about them using an &lt;em&gt;add&lt;/em&gt; method where it can make any protective assertions based on its state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overdraft&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_overdraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;overdraft&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transactions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;payee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;amt&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_transactions&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_overdrawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_overdraft&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;available_credit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_overdraft&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current Balance: %s"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_overdrawn&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(Overdrawn by %s)"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available_credit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Beware the paperboy
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://media.pragprog.com/articles/jan_03_enbug.pdf"&gt;“The Art of Enbugging”&lt;/a&gt; there is a great analogy for this principle.&lt;/p&gt;

&lt;p&gt;A paperboy comes to the door to collect his payment for the week. He snatches the wallet from your back pocket, takes a wad of cash, and puts it back. This doesn’t sound quite right does it, but it’s how a lot of software is written. Some central logic bullies several “data” objects to do its bidding.&lt;/p&gt;

&lt;p&gt;More realistically, the paperboy would &lt;em&gt;tell&lt;/em&gt; you to pay and after some thought (validation) you should hand over the cash. This is how object-oriented code is supposed to work, with you looking after your affairs, the paperboy his, and communication taking place through messages passed back and forth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;‘Tell don’t ask’ is a good rule of thumb to help you structure your code well, and it doesn’t just apply to low-level code. The core message — behavior/data co-location — is equally as important for broader architecture too.&lt;/p&gt;

&lt;p&gt;It is, however, just a rule of thumb and as with many good design principles should be considered within the context of everything else. You will find that in certain circumstances the wider architecture dictates a more important structure (for example layering), and in that case, data co-location is just one factor of many to bear in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://media.pragprog.com/articles/jan_03_enbug.pdf"&gt;“The Art of Enbugging”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/AnemicDomainModel.html"&gt;Anemic Domain Model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/TellDontAsk.html"&gt;Tell Don’t Ask&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>codequality</category>
      <category>architecture</category>
    </item>
    <item>
      <title>“Finding Stuff” — Building a Great Search Experience</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Thu, 02 Dec 2021 18:55:06 +0000</pubDate>
      <link>https://dev.to/notmattlucas/finding-stuff-building-a-great-search-experience-2f9f</link>
      <guid>https://dev.to/notmattlucas/finding-stuff-building-a-great-search-experience-2f9f</guid>
      <description>&lt;p&gt;Search is everywhere. How would you survive a day without it!? It’s on every website and part of every product. Whether you’re purchasing underwear, trawling Netflix for a Friday night movie, or even leading a cutting-edge research team, it can be a modern miracle to summon what you need at the cost of a few key presses.&lt;/p&gt;

&lt;p&gt;Despite being ubiquitous, why is search so often terrible? It’s frequently seen as second class, a bonus. It is plugged in and expected to work out of the box. It takes time, thought, and effort to craft a well-balanced search experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before We Begin
&lt;/h2&gt;

&lt;p&gt;Talking about concepts without an actual example to stare at can often become a challenge. For this post, we’ll be using a sample of around 10,000 books sourced from the &lt;a href="https://www.goodreads.com/"&gt;Goodreads&lt;/a&gt; website. The data set itself, and scripts to load it into &lt;a href="https://www.elastic.co/elasticsearch/"&gt;Elasticsearch&lt;/a&gt;, can be found here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lucas-matt/search_playground"&gt;https://github.com/lucas-matt/search_playground&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Principle 1. Search Engines Are Dumb, You Are Not
&lt;/h2&gt;

&lt;p&gt;Often search engines are seen as just another database. They do look and act similar in many ways, but once you scratch the surface, you reveal a wealth of fancy algorithms and highly optimized data structures that differ quite drastically from a standard data store.&lt;/p&gt;

&lt;p&gt;There are a number of parts to any search tool that we need to understand so that we can build upon them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;analysis&lt;/strong&gt; (zapping a chunk of text into fragments)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;the inverted index&lt;/strong&gt; (a lightning-fast mapping of tokens to the documents in which they appear)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;the ranking algorithm&lt;/strong&gt; (identification and ordering of information based upon its relevance to the query)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Analysis
&lt;/h3&gt;

&lt;p&gt;As we push documents into our search engine, they get sliced and diced into lots of little chunks. These chunks, or tokens (as we should call them from now on), are the core currency of the search engine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q-iffQyi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmk9k99t3uvj2exmzy3h.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q-iffQyi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmk9k99t3uvj2exmzy3h.jpeg" alt="Tokens" width="700" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We control the way tokens get generated through an index’s schema definition. How we leverage this power has a huge effect on how the search engine behaves down the line — it’s one of the primary levers we can pull to master it. The choices are wide-ranging and include transformations such as token size (n-grams), removal of troubling stopwords (e.g., &lt;em&gt;the, and, this&lt;/em&gt;), and stemming of terms (e.g., &lt;em&gt;books to book&lt;/em&gt;). We will take a look at some of these in a moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Index
&lt;/h3&gt;

&lt;p&gt;Pick up any nonfiction textbook and flick to the back pages. Here we have a data structure that maps the key terms of the subject at hand to the passages in which they feature most heavily.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4QQ4fjOx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmkgairox836voapa0ze.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4QQ4fjOx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmkgairox836voapa0ze.jpeg" alt="Features" width="436" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;inverted index&lt;/em&gt; of a search engine behaves very similarly. At a simple level, it maps the tokens present in a document to the documents in which it occurs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ranking
&lt;/h3&gt;

&lt;p&gt;When you search for something, the engine matches your tokenized query against the index and scores each result using a ranking algorithm. Traditionally an algorithm such as &lt;a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf"&gt;TF-IDF&lt;/a&gt; is used. It rates a document higher the more frequently the document matches a term (TF, term frequency) offset by how common that term is (IDF, inverse document frequency).&lt;/p&gt;

&lt;p&gt;All records are given a score and pitted against one another to identify the winners: your search results.&lt;/p&gt;

&lt;h3&gt;
  
  
  So search engines are smart, right?
&lt;/h3&gt;

&lt;p&gt;Not exactly. Although the engineering underneath it all is a computer scientist’s dream, search engines are utter idiots! If the tokens generated from the query do not match any in the index exactly, you are out of luck. For example, if your index has &lt;strong&gt;horses&lt;/strong&gt; but the query analyzer generates &lt;strong&gt;horse&lt;/strong&gt;, too bad.&lt;/p&gt;

&lt;p&gt;Garbage in, garbage out! It’s your job to curate.&lt;/p&gt;

&lt;p&gt;Don’t just blindly add reams of data to the search engine. It’s your responsibility to turn the incoming mass of text into something useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Principle 2. It’s Not Just About Tokens but Features
&lt;/h2&gt;

&lt;p&gt;The tokens generated from a document are more than just items in an index. They represent the core ideas about it.&lt;/p&gt;

&lt;p&gt;What is a feature? Let’s take the example of the classic title Dracula from our dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dracula"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"genres"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"classics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"horror"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"fiction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"gothic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"paranormal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"vampire"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1897&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isbn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"393970124"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A Chronology and a Selected Bibliography are included."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Norton"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"average_rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ratings_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;618973&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"authors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Bram Stoker"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we see just a few of the features concerning the novel — it’s a gothic classic, published in 1897 by the author Bram Stoker, and has an average rating of 3.98/5.&lt;/p&gt;

&lt;p&gt;The source material will usually contain a wealth of features just sitting there ready to be picked, but often the most golden ones need to be mined from just beneath the surface. You need to model features to help communicate the ideas in your document. This technique is named &lt;strong&gt;feature extraction&lt;/strong&gt;. Here you can play the mad scientist, distilling the text down to its bare essentials, stripping away all the confusion and muddiness within.&lt;/p&gt;

&lt;p&gt;Feature engineering takes place during the analysis phase of indexing. The search engine has several tools to help us do this effectively. Let’s review a couple of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  n-grams
&lt;/h3&gt;

&lt;p&gt;The default tokenization strategy of any search engine is usually to (a) remove all punctuation and special characters, followed by (b) splitting on any whitespace. It can work quite well as an initial tactic because searching for a particular word will give you a successful match. For those long words that we have trouble spelling, however, this is not a great approach.&lt;/p&gt;

&lt;p&gt;Imagine searching for the book Cryptonomicon. Half of your users are just going to type “Crypton” and hit return. With the standard analyzer, this is not going to get very far. Remember that search engines are dumb, and an out-of-the-box strategy is only going to produce the single token Cryptonomicon, which will not match our search request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nHJpAxCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3uk47aospf57m790ixnb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nHJpAxCQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3uk47aospf57m790ixnb.jpeg" alt="Bad Search" width="700" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;N-grams, and in particular edge n-grams, are great for those fields in which you want to partially match. Take a look at the wealth of tokens generated by taking an n-grammed approach to “Cryptonomicon.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uCpynJIk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7mfgi3ux6oftgvm56e7d.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uCpynJIk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7mfgi3ux6oftgvm56e7d.jpeg" alt="Explode" width="700" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using this strategy, because &lt;em&gt;crypto&lt;/em&gt; is one of those tokens generated by the n-gram analyzer, we get our match.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OLNSWiR8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh3mjrpuv846gpa90x9b.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OLNSWiR8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh3mjrpuv846gpa90x9b.jpeg" alt="Good Search" width="700" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stemming
&lt;/h3&gt;

&lt;p&gt;Stemming involves getting to the root of a word. Many terms have the same core meaning: &lt;em&gt;quick, quickly,&lt;/em&gt; and &lt;em&gt;quicker&lt;/em&gt; all come from the same place, as do &lt;em&gt;happy, happier, happiest, and happiness&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---5IlbG7W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/targ96vlexemqd6dma4y.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---5IlbG7W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/targ96vlexemqd6dma4y.jpeg" alt="Stemming" width="700" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By reducing a term to its stem, you are distilling it to its core essence. Utilizing this approach, you can translate those thoughts in the head of a user into a successful search result. You’re allowing them to converse at the level of ideas rather than worrying about syntax.&lt;/p&gt;

&lt;p&gt;For a more detailed dive into stemming, check out the following section in &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/stemming.html"&gt;Elasticsearch’s documentation&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Principle 3. Tuning In to Signals
&lt;/h2&gt;

&lt;p&gt;Feature extraction is paramount to building a well-tuned search experience, but you must always keep the end user in mind while tackling it. What are the use cases for search? What kinds of items are users likely to be tracking down? What properties are most helpful in identifying or suggesting this? How can we manipulate features for maximum effect?&lt;/p&gt;

&lt;p&gt;We must understand the user and anticipate their intent. One approach to thinking about this (as described in the book &lt;a href="https://www.manning.com/books/relevant-search"&gt;Relevant Search&lt;/a&gt;) is the strength of a signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signals
&lt;/h3&gt;

&lt;p&gt;When someone makes a search request, they’re hunting for documents that resonate most strongly with their wants. They are giving us several clues to help us help them: author, genre, title, year, etc. The documents that align most closely with the concepts respond with the strongest signal (as measured by the relevance score).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CcwVKGJV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mayr4dwvno9qerp9z9px.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CcwVKGJV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mayr4dwvno9qerp9z9px.jpeg" alt="Signals" width="700" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not all signals are as clear as others, and some may actively work against you. While one feature may allow for a resounding match, others may generate noise, causing a false positive and detracting from the overall quality of the search results. We have the tools, however, to amplify those signals that are crystal clear and to quieten those that may occasionally be useful but more often than not get in the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Boosting
&lt;/h3&gt;

&lt;p&gt;It is commonly the case that a hit on one property (title, for example) is much more valuable than on another (e.g., description). We can influence the search engine to take our preferences into account.&lt;/p&gt;

&lt;p&gt;By raising one field above another, it increases the importance of its match over a peer. Those that resonate with your query will do so much more clearly, and those false positives will fade into insignificance. Boosting one field over another is as simple as assigning it a positive number.&lt;/p&gt;

&lt;p&gt;Let’s say we fancy a classic Charles Dickens book, and we feed our search algorithm with the term &lt;em&gt;dickens&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nHHqbyi9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qicwu9fkb81situvakc5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nHHqbyi9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qicwu9fkb81situvakc5.jpeg" alt="Dickens" width="700" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results are a mixed bag. Some are Charles Dickens books, for sure, were but beaten to the podium by a number of others. Your users may think it strange for Moby-Dick to win this competition! We can change that outcome by levelling the playing field and adding a boost to the authors field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mx3SaJ0f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2let4g85k53vxnbj659.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mx3SaJ0f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2let4g85k53vxnbj659.jpeg" alt="Dickens Boost" width="700" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Names are often a more precise match than something in a title or description — especially a full name, but we’ll see that later. By adding a boost to the authors field, we’re emphasising those records to show as more relevant to our search.&lt;/p&gt;

&lt;p&gt;A word of warning though. It is a bit of an art weighting and balancing fields. Overly promote and you may end up fixing one problem (e.g., searching for the title) but introduce another (e.g., ability to search by publisher).&lt;/p&gt;

&lt;h3&gt;
  
  
  Match difficulty and boosting
&lt;/h3&gt;

&lt;p&gt;One solution to the balancing problem is to correlate the size of the weight with the difficulty of obtaining a winning match on the field.&lt;/p&gt;

&lt;p&gt;Properties that provide us with a crystal-clear signal include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ID&lt;/li&gt;
&lt;li&gt;Full name&lt;/li&gt;
&lt;li&gt;ISBN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hit upon one of these and you’re on to a winner. Match against the description field, however, and you can never be completely confident because of the wide breadth of the content.&lt;/p&gt;

&lt;p&gt;You can sleep easier elevating a feature that’s almost akin to a binary signal: It either matches or it doesn’t.&lt;/p&gt;

&lt;h3&gt;
  
  
  Same text, different analysis
&lt;/h3&gt;

&lt;p&gt;Consider the author’s name. There are at least two different signals you can see here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Partial match, e.g., surname: King&lt;/li&gt;
&lt;li&gt;Complete match, e.g., full name: Stephen King&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latter signal is clearly of more significance than the former. It is much more valuable for us to know that the user is looking for an author by their full name. However, that doesn’t mean a partial match isn’t valuable at all, even if it could just be coincidental — you don’t always know the exact spelling of an author’s full name.&lt;/p&gt;

&lt;p&gt;How can we handle both cases when we have but one name field? We can use feature modelling here to split this into two separate fields. Each feature can be tuned independently, the balance giving higher precedence to the more thorough match but allowing the weaker one to add value when appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-matching
&lt;/h3&gt;

&lt;p&gt;You do not always look for just one feature, but often mash a ton of different clues into one search string. You might search not only for an author but also specific subject matter from that author — Christmas novels by Charles Dickens, for instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zu2-RVn3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmj3yjvs1xcioomganp5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zu2-RVn3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmj3yjvs1xcioomganp5.jpeg" alt="Search" width="700" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using a &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html"&gt;best-fields&lt;/a&gt; approach, Elasticsearch is going to pick the best match of the bunch: the signal giving us the strongest reaction. This approach can get in a bit of a muddle with the cross-feature query we just saw. What we want is to match primarily against the strongest signal, &lt;em&gt;Christmas&lt;/em&gt;, and then augment this with a second field to emphasize its importance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ap0E3wXI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x6tsx9g4dlfooqibdysw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ap0E3wXI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x6tsx9g4dlfooqibdysw.jpeg" alt="Search" width="700" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By setting up a tie-breaker, a value between 0 and 1, the search engine will still proceed to match on the best field, but then it will go one step further and use the other signals to further refine the results. In our example, Christmas could be our primary hit, with &lt;em&gt;Dickens&lt;/em&gt; adding enough additional weight to hoist those novels into first place.&lt;/p&gt;




&lt;h2&gt;
  
  
  Principle 4. Feedback, feedback, feedback
&lt;/h2&gt;

&lt;p&gt;Search isn’t a one-way street. It’s not &lt;em&gt;just&lt;/em&gt; about handling a search request and blindly serving the user; it’s critical to have a conversation with them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2NXHKFpi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mnkz7zccymnp4tlnyyeq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2NXHKFpi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mnkz7zccymnp4tlnyyeq.jpeg" alt="Knows More" width="700" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Suggestions
&lt;/h3&gt;

&lt;p&gt;Often the user does not quite know what they’re seeking. They bash out a vague notion of what they want on the keyboard and see what bounces back. Without a little assistance, this can be a bit of a shot in the dark. They try, fail, and try again until they either flounder into something usable or give up.&lt;/p&gt;

&lt;p&gt;By presenting auto-completing suggestions, you’re giving immediate feedback on the kinds of things available, with the bonus of not needing perfect recall or spelling ability.&lt;/p&gt;

&lt;p&gt;Suggestions are supported out of the box by most, if not all, search engines. In Elasticsearch, it is just another (albeit special) field into which you copy your titles, author names, genres, and the like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z6HS8Fx9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o3tc48qtgmiie71ez7l.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z6HS8Fx9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4o3tc48qtgmiie71ez7l.jpeg" alt="Suggestions" width="700" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Highlighting
&lt;/h3&gt;

&lt;p&gt;Have you ever searched for something and wondered how the heck you got the results you did? If your documents are data-rich, it can sometimes become unclear what you’ve hit when searching.&lt;/p&gt;

&lt;p&gt;Providing feedback by highlighting the terms that were matched can turn out to be invaluable, allowing the user to tweak their request to be more specific or to filter out unwanted results.&lt;/p&gt;

&lt;p&gt;For more information, read about how to highlight in the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/highlighting.html"&gt;Elasticsearch documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and iteration
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“What gets measured gets managed” — Peter Drucker&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Building a truly useful search experience requires learning from your users. Domain expertise is clearly important, but actually seeing what your users come up with can reveal some key insights.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bouncing around?&lt;/strong&gt; Search after search after search and still not sticking on any one result. Why is this? What terms are being looked for?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No results?&lt;/strong&gt; Are a lot of different users looking for something that doesn’t return anything useful? Is your search engine badly tuned, or maybe there’s an opportunity to be pounced upon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A successful outcome?&lt;/strong&gt; Did the session have a successful outcome? Was something bought, watched, read?&lt;/p&gt;

&lt;p&gt;Gather and analyze this information to continually improve your solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Last Word
&lt;/h2&gt;

&lt;p&gt;We’ve touched upon quite a few different aspects that need to be considered when putting together an engaging search experience. We’ve only just scratched the surface here, and I’d advise checking out the resources below for more information.&lt;/p&gt;

&lt;p&gt;Search is a complete subject area in its own right, branching out at the more cutting edges into related topics, including recommendations and machine learning. As with most engineering projects, a successful outcome can be achieved by approaching it in small steps. Make sure you start simple, collect feedback, iterate, and evolve as required.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/relevant-search"&gt;Relevant Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.manning.com/books/elasticsearch-in-action"&gt;Elasticsearch in Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lucas-matt/search_playground"&gt;https://github.com/lucas-matt/search_playground&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>search</category>
      <category>architecture</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dependency Inversion Principle</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Wed, 24 Nov 2021 19:50:33 +0000</pubDate>
      <link>https://dev.to/notmattlucas/dependency-inversion-principle-2gjo</link>
      <guid>https://dev.to/notmattlucas/dependency-inversion-principle-2gjo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“The most flexible systems are those in which dependencies refer only to abstractions and not concretions”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An application is almost always comprised of some core business logic triggering real-world outcomes through a set of utilities. A report generator exporting to a spreadsheet or railway traffic system controlling stop-lights being two such examples.&lt;/p&gt;

&lt;p&gt;The flow of control of a program is often from core logic to more specialized utilities, such as I/O.&lt;/p&gt;

&lt;p&gt;If the source code dependencies flow in this same direction, however, it can result in tight coupling of business logic with lower-level concerns. As low-level implementations are likely to change often, this will be a source of volatility within an application.&lt;/p&gt;

&lt;p&gt;To protect against volatility, we want any high-level concerns to depend on abstractions rather than concretions — that is, interfaces rather than implementations. Interfaces change much less frequently than their instantiations, and so referencing these will shield a client from uncertainty.&lt;/p&gt;

&lt;p&gt;Of course, something has to know about the dirty detail of implementation, and that’s where dependency injection helps.&lt;/p&gt;

&lt;p&gt;Rather than core logic knowing what to use directly, you inject it with “an implementation” ensuring the source is as ignorant as possible to those low-level concerns. The grubby wiring can be tucked away in a few key places, such as factories or dependency injection frameworks, e.g. Spring.&lt;/p&gt;

&lt;p&gt;To conclude this principle, it’s worth reiterating a few points listed by Uncle Bob concerning the DIP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t refer to volatile concrete classes.&lt;/li&gt;
&lt;li&gt;Don’t derive (inherit) from volatile concrete classes.&lt;/li&gt;
&lt;li&gt;Don’t override concrete functions.&lt;/li&gt;
&lt;li&gt;Never mention the name of anything concrete and volatile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Originally published at &lt;a href="https://betterprogramming.pub/revisiting-solid-927e6a5202d3"&gt;Better Programming&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Interface Segregation Principle</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Wed, 24 Nov 2021 19:48:16 +0000</pubDate>
      <link>https://dev.to/notmattlucas/interface-segregation-principle-37eg</link>
      <guid>https://dev.to/notmattlucas/interface-segregation-principle-37eg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“No client should be forced to depend on methods it does not use”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Conceptually the simplest of the principles and one that is closely related to the SRP, the interface segregation principle is about finding the most appropriate abstractions in your code. The reason for this, is again, to protect against one avenue of change affecting other orthogonal ones.&lt;/p&gt;

&lt;p&gt;Having to depend on an interface that has muddled concerns can lead to a lot of unwanted baggage. The bloat can leak into your code as you have to cater for cases you don’t care about. Upgrades come thick and fast but for features that you don’t use and with the risk that those changes will break those you do.&lt;/p&gt;

&lt;p&gt;Overly complex interfaces are usually grown rather than designed. The slow drip feed of additional requirements, odd tweaks, and little hacks can snowball over the years to create a monster.&lt;/p&gt;

&lt;p&gt;Strong interface and API design is an exercise in restraint. Cohesion is your guide here. Keep your interfaces cohesive, collecting together those concerns that change together, and free your clients from an extra burden.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://betterprogramming.pub/revisiting-solid-927e6a5202d3"&gt;Better Programming&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Liskov Substitution Principle</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Wed, 24 Nov 2021 19:45:42 +0000</pubDate>
      <link>https://dev.to/notmattlucas/liskov-substitution-principle-5hip</link>
      <guid>https://dev.to/notmattlucas/liskov-substitution-principle-5hip</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“If for each object o1 of type S, there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ouch! That’s a bit of a mouthful. Let’s forget the computer science-y language for a moment and focus on the core point. The Liskov substitution principle is all about doing inheritance well.&lt;/p&gt;

&lt;p&gt;It tells us not to create subtypes that, despite conforming correctly at a contract level, diverge wildly from the true semantics creating some nasty surprises in the behavior of our program in the process.&lt;/p&gt;

&lt;p&gt;The usual example used (and I’ll do no different through sheer laziness) is that of the square/rectangle problem. To begin, let’s say we have a type, &lt;code&gt;Rectangle&lt;/code&gt;, with two methods &lt;code&gt;#setX&lt;/code&gt; and &lt;code&gt;#setY&lt;/code&gt; to assign the lengths of the two dimensions appropriately — so far so good.&lt;/p&gt;

&lt;p&gt;Now, what if we create a subclass for the special case of a &lt;code&gt;Square&lt;/code&gt; in which X and Y must always be equal?&lt;/p&gt;

&lt;p&gt;If we have a program that uses &lt;code&gt;Rectangle&lt;/code&gt;, and then all of a sudden we sneak in the &lt;code&gt;Square&lt;/code&gt; subtype, things could get a bit strange when we call &lt;code&gt;#setX&lt;/code&gt; and it changes Y too (or vice versa). The subtype has to make sense in the shadow of its parent.&lt;/p&gt;

&lt;p&gt;This isn’t limited to just inheritance but also makes sense in ensuring the robustness of any interface, be it an API, REST call, or something else.&lt;/p&gt;

&lt;p&gt;Substitution of any of these for another should be seamless, going unnoticed by any caller. If the client has to hack in a workaround to handle different implementations then we’ve botched the point of having a contract.&lt;/p&gt;

&lt;p&gt;Respecting this principle, however, can often be quite difficult. Many interfaces aren’t quite as abstract as they really should be, leaking some implementation detail.&lt;/p&gt;

&lt;p&gt;This is especially true when it comes to the things you may not always really consider, like exceptions.&lt;/p&gt;

&lt;p&gt;To avoid setting this trap yourself, try and keep your interface declarations as simple as you possibly can — other developers will thank you.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://betterprogramming.pub/revisiting-solid-927e6a5202d3"&gt;Better Programming&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Open/Closed Principle</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Wed, 24 Nov 2021 19:08:30 +0000</pubDate>
      <link>https://dev.to/notmattlucas/openclosed-principle-3apa</link>
      <guid>https://dev.to/notmattlucas/openclosed-principle-3apa</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“A software artifact should be open for extension but closed for modification.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Extension, so this must be about inheritance? Sure enough, in Bertrand Meyer’s original incarnation, that was the case but hold on there!&lt;/p&gt;

&lt;p&gt;Over the past 30 years, we’ve learned that inheritance is quite a tricky beast to tame, so we might not want to take that route right away.&lt;/p&gt;

&lt;p&gt;What do we mean by preventing modification and allowing for extension? If you have to change reams of existing code every time you make a small alteration then you know there’s something wrong with your architecture — it isn’t closed for modification.&lt;/p&gt;

&lt;p&gt;Rather than changing what’s already there, you should be able to build on top of a robust foundation through extension. Adding new features should ideally be easy and low risk.&lt;/p&gt;

&lt;p&gt;Extension itself has more than one form. Firstly, you have the traditional mode of inheritance. This, as mentioned, comes with its own set of problems (discussed elsewhere).&lt;/p&gt;

&lt;p&gt;Alternatively, you can inject additional behavior into high-level components. Interfaces are the perfect tool for extension through injection. They protect against modification by presenting a well-defined contract that controls change.&lt;/p&gt;

&lt;p&gt;They then allow us to extend our software by providing and plugging in new implementations of this contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9rpl8pe2keald1nq44n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9rpl8pe2keald1nq44n.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The use of interfaces here ensures a robust hierarchy where those important high-level components are protected from changes in lower-level ones. When that next feature request comes along you’ll be ready to slot it in without touching those precious existing functions.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://betterprogramming.pub/revisiting-solid-927e6a5202d3" rel="noopener noreferrer"&gt;Better Programming&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Single Responsibility Principle</title>
      <dc:creator>Matthew Lucas</dc:creator>
      <pubDate>Wed, 24 Nov 2021 19:01:55 +0000</pubDate>
      <link>https://dev.to/notmattlucas/single-responsibility-principle-4dfn</link>
      <guid>https://dev.to/notmattlucas/single-responsibility-principle-4dfn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Gather together the things that change for the same reasons. Separate those things that change for different reasons.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s a common misconception to simplify this as &lt;em&gt;“every module should do just one thing”&lt;/em&gt;. That’s not quite right because it’s not the “doing” that’s the point.&lt;/p&gt;

&lt;p&gt;Boiling your code down to the simplest possible components can lead to horribly disjointed software. It’s the reason to change that’s key rather than what it’s doing, but what defines a reason to change?&lt;/p&gt;

&lt;p&gt;The principle is primarily about people. Those users and stakeholders that push for new features are those that drive change in your software, and it’s specifically the scope of that change that we’re looking to limit.&lt;/p&gt;

&lt;p&gt;It’s frustrating for both users and developers when a reasonable tweak in one concern leaks into, and breaks, another. You’ll want to separate the responsibilities to avoid exactly this and to reduce the mental burden required by a developer when dealing with just one piece.&lt;/p&gt;

&lt;p&gt;The single responsibility principle applies across the whole stack, from minute detail to high-level abstraction. It holds when building granular functions or coarse components and in determining architectural boundaries.&lt;/p&gt;

&lt;p&gt;It may be seen under a different guise, for example, the common closure principle which itself advises that components should be deployed according to change and that any change should effect a minimal set of components (ideally one).&lt;/p&gt;

&lt;p&gt;Much of the SRP can be seen as an answer to a development scalability problem.&lt;/p&gt;

&lt;p&gt;Separate those areas that can change for different reasons and you can scale out your engineers without them treading on each other’s toes or scale-up teams without blocking dependencies and bottlenecks between them.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://betterprogramming.pub/revisiting-solid-927e6a5202d3"&gt;Better Programming&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
