CINXE.COM
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><meta name="twitter:card" content="summary_large_image" data-next-head=""/><meta name="twitter:site" content="@appsignal" data-next-head=""/><meta name="twitter:creator" content="@appsignal" data-next-head=""/><meta content="AppSignal" name="publisher" data-next-head=""/><meta content="2021 AppSignal" name="copyright" data-next-head=""/><meta content="https://blog.appsignal.com" name="host" data-next-head=""/><meta content="Middleman App" name="generator" data-next-head=""/><title data-next-head="">Using Dependency Injection in Elixir | AppSignal Blog</title><meta name="robots" content="index,follow,max-image-preview:large" data-next-head=""/><meta name="description" content="Dependency injection can prove useful in Elixir. In this first part of a two-part series, we'll look at some basic concepts, core principles, and types of dependency injection." data-next-head=""/><meta property="og:title" content="Using Dependency Injection in Elixir | AppSignal Blog" data-next-head=""/><meta property="og:description" content="Dependency injection can prove useful in Elixir. In this first part of a two-part series, we'll look at some basic concepts, core principles, and types of dependency injection." data-next-head=""/><meta property="og:url" content="https://blog.appsignal.com/2024/05/21/using-dependency-injection-in-elixir.html" data-next-head=""/><meta property="og:type" content="article" data-next-head=""/><meta property="og:image" content="https://ondemand.bannerbear.com/signedurl/Mn62mqoVbWvyB5wgQ1/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IlVzaW5nIERlcGVuZGVuY3kgSW5qZWN0aW9uIGluIEVsaXhpciJ9LHsibmFtZSI6ImltYWdlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly9hcHBzaWduYWwtbmV4dGpzLWJsb2ctbDRkNDR4OGx5LWFwcHNpZ25hbC52ZXJjZWwuYXBwL2ltYWdlcy9ibG9nLzIwMjQtMDUvZGVwZW5kZW5jeS1pbmplY3Rpb24tcDEuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLWw0ZDQ0eDhseS1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d&s=c99c1e372af5921cc9113eb727618a422207ff7cf155970814dcbcb9f917057f" data-next-head=""/><meta property="og:image:alt" content="Using Dependency Injection in Elixir" data-next-head=""/><meta property="og:image:type" content="image/jpeg" data-next-head=""/><meta property="og:image:width" content="1200" data-next-head=""/><meta property="og:image:height" content="628" data-next-head=""/><link rel="canonical" href="https://blog.appsignal.com/2024/05/21/using-dependency-injection-in-elixir.html" data-next-head=""/><meta name="author" content="Allan MacGregor" data-next-head=""/><meta name="article:published_time" content="2024-05-21T00:00:00+00:00" data-next-head=""/><link rel="alternate" type="application/rss+xml" title="AppSignal" href="https://blog.appsignal.com/category/elixir-feed.xml" data-next-head=""/><link rel="icon" href="/favicon/favicon.svg" type="image/svg+xml"/><link rel="mask-icon" href="/favicon/favicon.svg" color="#29A575"/><link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"/><link rel="manifest" href="/favicon/manifest.webmanifest"/><link rel="preload" href="/fonts/rubik-v12-latin-regular.woff2" as="font" type="font/woff2" crossorigin="anonymous"/><link rel="preload" href="/fonts/rubik-v12-latin-500.woff2" as="font" type="font/woff2" crossorigin="anonymous"/><meta name="ahrefs-site-verification" content="eaeabe5fcccf5783b1b0c574c52a3306af55ea1ab27e5eed6405393ed63feae6"/><link rel="preload" href="/_next/static/css/d68a48dc586780e0.css" as="style"/><script type="application/ld+json" data-next-head="">{"@context":"https://schema.org","@type":"BlogPosting","datePublished":"2024-05-21T00:00:00+00:00","description":"Dependency injection can prove useful in Elixir. In this first part of a two-part series, we'll look at some basic concepts, core principles, and types of dependency injection.","mainEntityOfPage":{"@type":"WebPage","@id":"https://blog.appsignal.com/2024/05/21/using-dependency-injection-in-elixir.html"},"headline":"Using Dependency Injection in Elixir","image":["https://ondemand.bannerbear.com/signedurl/Mn62mqoVbWvyB5wgQ1/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IlVzaW5nIERlcGVuZGVuY3kgSW5qZWN0aW9uIGluIEVsaXhpciJ9LHsibmFtZSI6ImltYWdlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly9hcHBzaWduYWwtbmV4dGpzLWJsb2ctbDRkNDR4OGx5LWFwcHNpZ25hbC52ZXJjZWwuYXBwL2ltYWdlcy9ibG9nLzIwMjQtMDUvZGVwZW5kZW5jeS1pbmplY3Rpb24tcDEuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLWw0ZDQ0eDhseS1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d&amp;s=c99c1e372af5921cc9113eb727618a422207ff7cf155970814dcbcb9f917057f"],"dateModified":"2024-05-21T00:00:00+00:00","author":[{"@type":"Person","name":"Allan MacGregor","url":"https://blog.appsignal.com/authors/allan-macgregor.html"}],"publisher":{"@type":"Organization","name":"AppSignal","logo":{"@type":"ImageObject","url":"https://appsignal.com/images/logo-appsignal.svg"}}}</script><link rel="preload" as="image" imageSrcSet="/_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=3840&q=90 3840w" imageSizes="(min-width: 1024px) 1200px, 100vw" data-next-head=""/><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css" integrity="sha384-Um5gpz1odJg5Z4HAmzPtgZKdTBHZdw8S29IecapCSB31ligYPhHQZMIlWLYQGVoc" crossorigin="anonymous"/><link rel="stylesheet" href="/_next/static/css/d68a48dc586780e0.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script defer="" src="/_next/static/chunks/805.172fd4a021ced064.js"></script><script defer="" src="/_next/static/chunks/367.12b714d893310efc.js"></script><script defer="" src="/_next/static/chunks/253.4bd68a22f36e4b7b.js"></script><script defer="" src="/_next/static/chunks/863.ce323ba275b06481.js"></script><script src="/_next/static/chunks/webpack-c1b2eba93bbf92fc.js" defer=""></script><script src="/_next/static/chunks/framework-3707fc5f85e96a29.js" defer=""></script><script src="/_next/static/chunks/main-4d73d62b260d8d7d.js" defer=""></script><script src="/_next/static/chunks/pages/_app-d8433ae4de68eaac.js" defer=""></script><script src="/_next/static/chunks/587-a61855beef727d74.js" defer=""></script><script src="/_next/static/chunks/281-4ec35fbeaa39295c.js" defer=""></script><script src="/_next/static/chunks/335-8a5ac53490a88db9.js" defer=""></script><script src="/_next/static/chunks/pages/%5Byear%5D/%5Bmonth%5D/%5Bday%5D/%5Btext%5D-c5c0e35a1339e5da.js" defer=""></script><script src="/_next/static/Slyvv84b2YSnVGyGXaxYw/_buildManifest.js" defer=""></script><script src="/_next/static/Slyvv84b2YSnVGyGXaxYw/_ssgManifest.js" defer=""></script></head><body class="font-rubik"><link rel="preload" as="image" imageSrcSet="/_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=3840&q=90 3840w" imageSizes="(min-width: 1024px) 1200px, 100vw"/><div id="__next"><!--$--><!--/$--><header class="sticky z-40 top-0 w-full xl:overflow-y-visible bg-gray-900 text-white border-b border-b-gray-800 border-b border-b-gray-800"><div class="c-container flex justify-between items-center py-4 false"><a href="https://www.appsignal.com" class="flex"><div class="w-[130px] h-[24px]"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350.92 64.54"><g fill="#fff"><path d="M341.56 12.58c-5.46 0-9.37 4.82-13.39 10.18C325.68 11.38 322.9 0 314.07 0S302.46 11.38 300 22.76c-4-5.36-7.93-10.18-13.39-10.18-3.47 0-9.35 2.37-9.35 11.22v16.11c0 8.69 4.64 10.82 7.42 11.54 4.8 1.25 16.36 2.4 29.37 2.4s24.57-1.15 29.36-2.4c2.79-.72 7.43-2.85 7.43-11.54V23.8c0-8.85-5.88-11.22-9.36-11.22M314.07 5c5.29 0 7.3 10.44 10.12 23-3.09 3.79-6.36 6.86-10.12 6.86S307 31.78 304 28c2.63-11.72 4.65-23 10.12-23m-31.84 34.91V23.8c0-5.59 3-6.2 4.33-6.2 3.85 0 8 6.43 11.94 11.59-2.83 11.71-5.78 18.81-12.75 17.37-1.82-.5-3.52-1.65-3.52-6.65M296 48.05c3-3.35 5-8.58 6.5-14.12 3.23 3.38 6.95 5.93 11.57 5.93s8.33-2.55 11.56-5.93c1.5 5.54 3.47 10.77 6.55 14.12-5.39.5-11.88.79-18.11.79s-12.73-.29-18.12-.79m49.91-8.14c0 5-1.71 6.15-3.52 6.65-7 1.48-9.93-5.66-12.72-17.37 3.95-5.16 8.09-11.59 11.94-11.59 1.3 0 4.34.61 4.34 6.2ZM1.13 52.07a1.12 1.12 0 0 1-.79-.34 1.16 1.16 0 0 1-.34-.85 1.52 1.52 0 0 1 .11-.52L14 13.74a2 2 0 0 1 .62-1 1.81 1.81 0 0 1 1.25-.4h5a1.81 1.81 0 0 1 1.25.4 2.44 2.44 0 0 1 .68 1l13.82 36.62a2.52 2.52 0 0 1 .06.52 1.16 1.16 0 0 1-.34.85 1.12 1.12 0 0 1-.79.34h-3.92a1.4 1.4 0 0 1-1.07-.37 2.41 2.41 0 0 1-.46-.7l-2.89-7.48H9.47L6.63 51a2 2 0 0 1-.48.66 1.5 1.5 0 0 1-1.1.37Zm10.15-14.18H25.4l-7.09-18.82ZM42.52 62.84a1.25 1.25 0 0 1-1.3-1.31V23.89a1.24 1.24 0 0 1 1.3-1.3h3.57a1.25 1.25 0 0 1 1.31 1.3v2.38a11.6 11.6 0 0 1 3.62-3A11.35 11.35 0 0 1 56.58 22a12.67 12.67 0 0 1 5.33 1 10.13 10.13 0 0 1 3.68 2.83 12.56 12.56 0 0 1 2.24 4.26 19.79 19.79 0 0 1 .88 5.21c0 .64.06 1.31.06 2s0 1.34-.06 2a18.1 18.1 0 0 1-.82 5.16 13.14 13.14 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.56 12.56 0 0 1-5.36 1.05 11.36 11.36 0 0 1-5.36-1.16 10.55 10.55 0 0 1-3.54-3v13a1.27 1.27 0 0 1-.37.94 1.38 1.38 0 0 1-1 .37ZM55 47.3a6.63 6.63 0 0 0 4.17-1.16 6.53 6.53 0 0 0 2.18-3 14.43 14.43 0 0 0 .8-4.14 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.8-4.14 6.58 6.58 0 0 0-2.18-3A6.69 6.69 0 0 0 55 27.35a6.44 6.44 0 0 0-6.49 4.22 12.43 12.43 0 0 0-.82 3.77q-.06 1-.06 2.16c0 .79 0 1.53.06 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86A6.83 6.83 0 0 0 55 47.3ZM75.71 62.84a1.27 1.27 0 0 1-1.3-1.31V23.89a1.25 1.25 0 0 1 1.3-1.3h3.58a1.29 1.29 0 0 1 .93.36 1.27 1.27 0 0 1 .37.94v2.38a11.53 11.53 0 0 1 3.63-3A11.29 11.29 0 0 1 89.77 22a12.64 12.64 0 0 1 5.33 1 10.16 10.16 0 0 1 3.69 2.83 12.74 12.74 0 0 1 2.21 4.3 20.21 20.21 0 0 1 .88 5.21v4a18.1 18.1 0 0 1-.83 5.16 12.94 12.94 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.53 12.53 0 0 1-5.36 1.05 11.31 11.31 0 0 1-5.35-1.16 10.49 10.49 0 0 1-3.55-3v13a1.27 1.27 0 0 1-.37.94 1.34 1.34 0 0 1-1 .37ZM88.19 47.3a6.1 6.1 0 0 0 6.35-4.19 14.43 14.43 0 0 0 .79-4.11 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.79-4.14 6.1 6.1 0 0 0-6.35-4.19 6.44 6.44 0 0 0-6.5 4.22 12.74 12.74 0 0 0-.82 3.77c0 .64-.05 1.36-.05 2.16s0 1.53.05 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86 6.84 6.84 0 0 0 4.09 1.1ZM122.17 52.63a21.18 21.18 0 0 1-8.56-1.53 12.82 12.82 0 0 1-5.27-4 9.44 9.44 0 0 1-1.93-5.25 1.1 1.1 0 0 1 .34-.79 1.15 1.15 0 0 1 .85-.34h4a1.47 1.47 0 0 1 1 .31 2.75 2.75 0 0 1 .57.76 6.36 6.36 0 0 0 1.36 2.39 7.72 7.72 0 0 0 2.86 2 12.06 12.06 0 0 0 4.73.79q4.48 0 6.61-1.53a4.88 4.88 0 0 0 2.12-4.19 3.83 3.83 0 0 0-1.13-2.87 10.56 10.56 0 0 0-3.52-2c-1.58-.6-3.64-1.25-6.17-1.93a36.52 36.52 0 0 1-7-2.55 10.29 10.29 0 0 1-4.19-3.57 9.81 9.81 0 0 1-1.39-5.44 9.72 9.72 0 0 1 1.73-5.67 11.66 11.66 0 0 1 4.93-3.94 18.66 18.66 0 0 1 7.68-1.45 19 19 0 0 1 6.35 1 14 14 0 0 1 4.57 2.55 11.53 11.53 0 0 1 2.77 3.4 8.31 8.31 0 0 1 1 3.57 1.18 1.18 0 0 1-.32.77 1.09 1.09 0 0 1-.87.37h-4.14a1.83 1.83 0 0 1-.88-.23 1.5 1.5 0 0 1-.65-.85 5.22 5.22 0 0 0-2.44-3.46 9.49 9.49 0 0 0-5.39-1.42 10.19 10.19 0 0 0-5.41 1.28 4.35 4.35 0 0 0-2.07 4 4.24 4.24 0 0 0 1 2.89 8.38 8.38 0 0 0 3.18 2 58.53 58.53 0 0 0 5.75 1.9 43.26 43.26 0 0 1 7.76 2.54 10.38 10.38 0 0 1 4.4 3.51 9.64 9.64 0 0 1 1.41 5.42 10 10 0 0 1-1.95 6.2 12.32 12.32 0 0 1-5.44 4 22 22 0 0 1-8.25 1.36ZM143.77 17.37a1.34 1.34 0 0 1-1-.37 1.24 1.24 0 0 1-.37-.93v-3.58a1.34 1.34 0 0 1 .37-1 1.32 1.32 0 0 1 1-.4h4.31a1.39 1.39 0 0 1 1 .4 1.29 1.29 0 0 1 .4 1v3.58a1.2 1.2 0 0 1-.4.93 1.41 1.41 0 0 1-1 .37Zm.34 34.7a1.38 1.38 0 0 1-1-.37 1.27 1.27 0 0 1-.37-.94V23.89a1.27 1.27 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.69a1.25 1.25 0 0 1 1.3 1.3v26.87a1.27 1.27 0 0 1-.37.94 1.28 1.28 0 0 1-.93.37ZM168.5 64.54a17.5 17.5 0 0 1-6.58-1.05 12.16 12.16 0 0 1-4-2.52 9.27 9.27 0 0 1-2.09-2.97 8.1 8.1 0 0 1-.65-2.38 1.13 1.13 0 0 1 .34-1 1.3 1.3 0 0 1 1-.39h3.86a1.51 1.51 0 0 1 .85.22 1.8 1.8 0 0 1 .56 1 11.19 11.19 0 0 0 1 1.73 5 5 0 0 0 1.93 1.61 7.69 7.69 0 0 0 3.57.68 11 11 0 0 0 4.05-.65 4.82 4.82 0 0 0 2.5-2.3 9.59 9.59 0 0 0 .87-4.47v-3.84a10.93 10.93 0 0 1-3.64 2.79 11.93 11.93 0 0 1-5.38 1.08 12.69 12.69 0 0 1-5.36-1 9.78 9.78 0 0 1-3.69-2.86 13.36 13.36 0 0 1-2.21-4.28 18.82 18.82 0 0 1-.82-5.27q-.06-1.53 0-3.12a18.94 18.94 0 0 1 .8-5.16 12.85 12.85 0 0 1 2.21-4.39 10.34 10.34 0 0 1 3.71-2.94 12.23 12.23 0 0 1 5.36-1.06 11.1 11.1 0 0 1 5.52 1.25 11.77 11.77 0 0 1 3.66 3.06V24a1.37 1.37 0 0 1 .37-1 1.31 1.31 0 0 1 1-.39h3.57a1.33 1.33 0 0 1 1.35 1.39v27.39a16.14 16.14 0 0 1-1.38 6.91 10 10 0 0 1-4.4 4.59 16.32 16.32 0 0 1-7.88 1.65Zm-.17-17.69a6.62 6.62 0 0 0 4.11-1.19 7.39 7.39 0 0 0 2.33-3 10.87 10.87 0 0 0 .87-3.71c0-.49.06-1.12.06-1.9s0-1.39-.06-1.84a11.09 11.09 0 0 0-.87-3.74 7.13 7.13 0 0 0-2.33-3 6.74 6.74 0 0 0-4.11-1.16 6.85 6.85 0 0 0-4.17 1.16 6.49 6.49 0 0 0-2.24 3 14.43 14.43 0 0 0-.79 4.14v2.84a14.62 14.62 0 0 0 .79 4.11 6.52 6.52 0 0 0 2.24 3.06 6.85 6.85 0 0 0 4.17 1.23ZM190.13 52.07a1.38 1.38 0 0 1-1-.37 1.31 1.31 0 0 1-.37-.94V23.89a1.31 1.31 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.63a1.25 1.25 0 0 1 1.31 1.3v2.44a12.29 12.29 0 0 1 3.8-3.09 11.91 11.91 0 0 1 5.72-1.24 11.34 11.34 0 0 1 6.12 1.59 10.28 10.28 0 0 1 3.92 4.41 15.55 15.55 0 0 1 1.37 6.77v16a1.25 1.25 0 0 1-.4.94 1.39 1.39 0 0 1-1 .37h-3.92a1.28 1.28 0 0 1-.93-.37 1.27 1.27 0 0 1-.37-.94V35.06a8.33 8.33 0 0 0-1.79-5.67 6.45 6.45 0 0 0-5.13-2 6.84 6.84 0 0 0-5.18 2 7.83 7.83 0 0 0-2 5.67v15.7a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37ZM230.92 52.63a11.76 11.76 0 0 1-5.25-1.16 9.93 9.93 0 0 1-3.74-3.09 7.31 7.31 0 0 1-1.39-4.38 7.42 7.42 0 0 1 3.18-6.27 18.1 18.1 0 0 1 8.39-3.09l8-1.19v-1.4a5.5 5.5 0 0 0-1.36-4c-.91-.94-2.42-1.41-4.54-1.41a6.9 6.9 0 0 0-3.74.9 5.22 5.22 0 0 0-2 2.44 1.32 1.32 0 0 1-1.25.74h-3.51a1.2 1.2 0 0 1-.94-.34 1.34 1.34 0 0 1-.31-.91 5.88 5.88 0 0 1 .71-2.12 8.28 8.28 0 0 1 2.07-2.5 11.76 11.76 0 0 1 3.66-2 15.47 15.47 0 0 1 5.35-.82 17.06 17.06 0 0 1 5.9.88 9.93 9.93 0 0 1 3.8 2.35 9.17 9.17 0 0 1 2.07 3.4 12.46 12.46 0 0 1 .65 4v18.1a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37h-3.63a1.25 1.25 0 0 1-1.3-1.31v-2.38a9.3 9.3 0 0 1-2 2 10.74 10.74 0 0 1-3 1.62 13.85 13.85 0 0 1-4.42.63Zm1.59-4.82a8.24 8.24 0 0 0 3.91-.93A6.74 6.74 0 0 0 239.2 44a10.38 10.38 0 0 0 1-4.88v-1.4l-6.13 1a13.25 13.25 0 0 0-5.52 1.75 3.66 3.66 0 0 0-1.85 3.06 3.46 3.46 0 0 0 .85 2.41 5.33 5.33 0 0 0 2.16 1.42 8 8 0 0 0 2.8.45ZM254.47 52.07a1.25 1.25 0 0 1-1.3-1.31V13.12a1.25 1.25 0 0 1 1.3-1.31h3.74a1.25 1.25 0 0 1 1.3 1.31v37.64a1.25 1.25 0 0 1-1.3 1.31Z"></path></g></svg></div></a><div class="hidden xl:block"><ul class="flex"><li><div class="block py-2 px-2 xl:px-3 no-underline cursor-pointer select-none text-gray-200 hover:text-white"><span>Features</span><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="angle-down" class="svg-inline--fa fa-angle-down inline-block pb-0.5 ml-2 text-gray-400 text-xxs" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M201.4 374.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 306.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z"></path></svg><div class="absolute mt-0 pt-2.5 z-10 w-72 hidden"><div class="w-72 px-3 py-4 bg-white shadow rounded-lg text-left"><h3 class="px-2 c_h-heading c_h-heading--caps text-sm tracking-wider text-gray-600 pb-2">Monitoring features</h3><ul><li><a href="https://www.appsignal.com/tour/errors/" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-orange-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Error tracking</span></a></li><li><a href="https://www.appsignal.com/tour/performance" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-green-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rabbit-running" class="svg-inline--fa fa-rabbit-running fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M460.7 39.3l-2.2-2.1c-4.7-4.6-11.5-6.4-17.9-4.7s-11.4 6.6-13.1 13l-.8 2.9c-3.3 11.8-5.1 23.7-5.5 35.6c24.3 20.6 42.9 47.8 53.3 78.8c-8.2-1.9-16.5-2.9-25-2.9c-.6 0-1.1 0-1.7 0c-18.4-44-56.5-77.4-103.6-89.3l-3.4-.9c-6.2-1.6-12.9-.6-18.5 2.6c-10.8 6.2-15.6 19.5-11.2 31.1c14.9 39.5 44.3 71.4 81.4 89.7c-3.7 6.6-6.4 14-7.6 21.8L279.7 154.9C248.8 137.3 213.8 128 178.2 128c-32.3 0-62.2 16.8-78.9 44.4C89 159.9 73.4 152 56 152c-30.9 0-56 25.1-56 56s25.1 56 56 56c11.3 0 21.8-3.3 30.6-9.1c4.9 17.2 14.5 33.1 28.4 45.9L257.2 431.2C269 442 284.4 448 300.4 448H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H352 335.2 320V340.5c0-42.9-28.4-80.5-69.6-92.3l-30.8-8.8c-8.5-2.4-13.4-11.3-11-19.8s11.3-13.4 19.8-11l30.8 8.8c55 15.7 92.8 65.9 92.8 123.1v15.7l56-32.4 6.4-3.7H515.7c33.3 0 60.3-27 60.3-60.3c0-18.1-8.2-35.3-22.2-46.7l-34.6-28.2c-4.6-3.7-9.4-7.1-14.5-10c8.4-49.3-7.8-100-43.9-135.5zm-318.5 382c-14.7 9.8-18.7 29.7-8.9 44.4s29.7 18.7 44.4 8.9l46-30.7-48.6-44.5-32.9 22zM480 240a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Performance monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/hosts" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-teal-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="server" class="svg-inline--fa fa-server fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Host monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/anomaly-detection" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-red-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="siren-on" class="svg-inline--fa fa-siren-on fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M69.3 36l48 32c11 7.4 14 22.3 6.7 33.3s-22.3 14-33.3 6.7l-48-32c-11-7.4-14-22.3-6.7-33.3s22.3-14 33.3-6.7zM597.3 76l-48 32c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l48-32c11-7.4 25.9-4.4 33.3 6.7s4.4 25.9-6.7 33.3zM24 192H88c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24zm528 0h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H552c-13.3 0-24-10.7-24-24s10.7-24 24-24zM144 352l25-199.9c4-32 31.2-56.1 63.5-56.1h175c32.3 0 59.5 24 63.5 56.1L496 352H234.1l21.8-174c1.1-8.8-5.1-16.8-13.9-17.9s-16.8 5.1-17.9 13.9L201.9 352H144zM96 416c0-17.7 14.3-32 32-32H512c17.7 0 32 14.3 32 32v32c0 17.7-14.3 32-32 32H128c-17.7 0-32-14.3-32-32V416z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Anomaly detection</span></a></li><li><a href="https://www.appsignal.com/tour/uptime-monitoring" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-yellow-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="traffic-light" class="svg-inline--fa fa-traffic-light fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V352c0 88.4 71.6 160 160 160s160-71.6 160-160V64c0-35.3-28.7-64-64-64H64zm96 416a48 48 0 1 1 0-96 48 48 0 1 1 0 96zm48-176a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zm-48-80a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Uptime monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/metrics" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-purple-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Metric dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/workflow" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-blue-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="users" class="svg-inline--fa fa-users fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Workflow</span></a></li><li><a href="https://www.appsignal.com/tour/log-management" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-hot-pink-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="line-columns" class="svg-inline--fa fa-line-columns fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M224 64c0-17.7-14.3-32-32-32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H192c17.7 0 32-14.3 32-32zm0 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM0 320c0 17.7 14.3 32 32 32H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM224 448c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM288 64c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 192c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32zM288 320c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 448c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Log management</span></a></li><li><a href="https://www.appsignal.com/tour/automated-dashboards" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-purple-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Automated Dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/check-ins" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-blue-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="square-check" class="svg-inline--fa fa-square-check fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Check-ins</span></a></li><li><a href="https://www.appsignal.com/tour/time-detective" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-azure-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user-secret" class="svg-inline--fa fa-user-secret fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 16c-6.7 0-10.8-2.8-15.5-6.1C201.9 5.4 194 0 176 0c-30.5 0-52 43.7-66 89.4C62.7 98.1 32 112.2 32 128c0 14.3 25 27.1 64.6 35.9c-.4 4-.6 8-.6 12.1c0 17 3.3 33.2 9.3 48H45.4C38 224 32 230 32 237.4c0 1.7 .3 3.4 1 5l38.8 96.9C28.2 371.8 0 423.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7c0-58.5-28.2-110.4-71.7-143L415 242.4c.6-1.6 1-3.3 1-5c0-7.4-6-13.4-13.4-13.4H342.7c6-14.8 9.3-31 9.3-48c0-4.1-.2-8.1-.6-12.1C391 155.1 416 142.3 416 128c0-15.8-30.7-29.9-78-38.6C324 43.7 302.5 0 272 0c-18 0-25.9 5.4-32.5 9.9c-4.8 3.3-8.8 6.1-15.5 6.1zm56 208H267.6c-16.5 0-31.1-10.6-36.3-26.2c-2.3-7-12.2-7-14.5 0c-5.2 15.6-19.9 26.2-36.3 26.2H168c-22.1 0-40-17.9-40-40V169.6c28.2 4.1 61 6.4 96 6.4s67.8-2.3 96-6.4V184c0 22.1-17.9 40-40 40zm-88 96l16 32L176 480 128 288l64 32zm128-32L272 480 240 352l16-32 64-32z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Time Detective</span></a></li></ul></div></div></div></li><li><div class="block py-2 px-2 xl:px-3 no-underline cursor-pointer select-none text-gray-200 hover:text-white"><span>Languages</span><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="angle-down" class="svg-inline--fa fa-angle-down inline-block pb-0.5 ml-2 text-gray-400 text-xxs" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M201.4 374.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 306.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z"></path></svg><div class="absolute mt-0 pt-2.5 z-10 w-72 hidden"><div class="w-72 px-3 py-4 bg-white shadow rounded-lg text-left"><h3 class="px-2 c_h-heading c_h-heading--caps text-sm tracking-wider text-gray-600 pb-2">Supported Languages</h3><ul><li><a href="https://www.appsignal.com/ruby" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-red-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gem" class="svg-inline--fa fa-gem fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M116.7 33.8c4.5-6.1 11.7-9.8 19.3-9.8H376c7.6 0 14.8 3.6 19.3 9.8l112 152c6.8 9.2 6.1 21.9-1.5 30.4l-232 256c-4.5 5-11 7.9-17.8 7.9s-13.2-2.9-17.8-7.9l-232-256c-7.7-8.5-8.3-21.2-1.5-30.4l112-152zm38.5 39.8c-3.3 2.5-4.2 7-2.1 10.5l57.4 95.6L63.3 192c-4.1 .3-7.3 3.8-7.3 8s3.2 7.6 7.3 8l192 16c.4 0 .9 0 1.3 0l192-16c4.1-.3 7.3-3.8 7.3-8s-3.2-7.6-7.3-8L301.5 179.8l57.4-95.6c2.1-3.5 1.2-8.1-2.1-10.5s-7.9-2-10.7 1L256 172.2 165.9 74.6c-2.8-3-7.4-3.4-10.7-1z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Ruby (on Rails) APM</span></a></li><li><a href="https://www.appsignal.com/elixir" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-purple-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="droplet" class="svg-inline--fa fa-droplet fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M192 512C86 512 0 426 0 320C0 228.8 130.2 57.7 166.6 11.7C172.6 4.2 181.5 0 191.1 0h1.8c9.6 0 18.5 4.2 24.5 11.7C253.8 57.7 384 228.8 384 320c0 106-86 192-192 192zM96 336c0-8.8-7.2-16-16-16s-16 7.2-16 16c0 61.9 50.1 112 112 112c8.8 0 16-7.2 16-16s-7.2-16-16-16c-44.2 0-80-35.8-80-80z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Elixir APM</span></a></li><li><a href="https://www.appsignal.com/nodejs" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-green-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="node-js" class="svg-inline--fa fa-node-js fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Node.js APM</span></a></li><li><a href="https://www.appsignal.com/javascript" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-yellow-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="square-js" class="svg-inline--fa fa-square-js fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96zM180.9 444.9c-33.7 0-53.2-17.4-63.2-38.5L152 385.7c6.6 11.7 12.6 21.6 27.1 21.6c13.8 0 22.6-5.4 22.6-26.5V237.7h42.1V381.4c0 43.6-25.6 63.5-62.9 63.5zm85.8-43L301 382.1c9 14.7 20.8 25.6 41.5 25.6c17.4 0 28.6-8.7 28.6-20.8c0-14.4-11.4-19.5-30.7-28l-10.5-4.5c-30.4-12.9-50.5-29.2-50.5-63.5c0-31.6 24.1-55.6 61.6-55.6c26.8 0 46 9.3 59.8 33.7L368 290c-7.2-12.9-15-18-27.1-18c-12.3 0-20.1 7.8-20.1 18c0 12.6 7.8 17.7 25.9 25.6l10.5 4.5c35.8 15.3 55.9 31 55.9 66.2c0 37.8-29.8 58.6-69.7 58.6c-39.1 0-64.4-18.6-76.7-43z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">JavaScript Error Tracking</span></a></li><li><a href="https://www.appsignal.com/python" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-blue-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="python" class="svg-inline--fa fa-python fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Python APM</span></a></li></ul></div></div></div></li><li><a href="https://www.appsignal.com/learning-center" class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white ">Learn</a></li><li><a href="https://docs.appsignal.com" class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white ">Docs</a></li><li><a class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white " href="/">Blog</a></li><li><a href="https://www.appsignal.com/plans" class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white ">Pricing</a></li></ul></div><ul class="hidden xl:flex justify-end space-x-4 dark"><li><a href="https://appsignal.com/users/sign_in" class="c-button c-button--sm c-button--white dark:c-button--gray">Login</a></li><li><a href="https://appsignal.com/users/sign_up" class="c-button c-button--sm">Start free trial</a></li></ul><div class="xl:hidden dark"><a href="#menu" class="c-button c-button--sm c-button--white dark:c-button--gray"><div class="flex item-center space-x-2 pr-0.5"><div>Menu</div><div class="inline-block w-2 pt-0.25"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bars" class="svg-inline--fa fa-bars " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"></path></svg></div></div></a></div></div><nav class="hidden border-t border-gray-700"><div class="dark c-container h-screen overflow-y-auto pt-7 pb-40"><div><h3 class="c_h-heading c_h-heading--caps text-sm text-gray-200 pb-3 mb-3 border-b border-gray-700">Monitoring features</h3><ul class="pb-8 space-y-1"><li><a href="https://www.appsignal.com/tour/errors/" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"></path></svg></figure><span>Error tracking</span></a></li><li><a href="https://www.appsignal.com/tour/performance" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rabbit-running" class="svg-inline--fa fa-rabbit-running " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M460.7 39.3l-2.2-2.1c-4.7-4.6-11.5-6.4-17.9-4.7s-11.4 6.6-13.1 13l-.8 2.9c-3.3 11.8-5.1 23.7-5.5 35.6c24.3 20.6 42.9 47.8 53.3 78.8c-8.2-1.9-16.5-2.9-25-2.9c-.6 0-1.1 0-1.7 0c-18.4-44-56.5-77.4-103.6-89.3l-3.4-.9c-6.2-1.6-12.9-.6-18.5 2.6c-10.8 6.2-15.6 19.5-11.2 31.1c14.9 39.5 44.3 71.4 81.4 89.7c-3.7 6.6-6.4 14-7.6 21.8L279.7 154.9C248.8 137.3 213.8 128 178.2 128c-32.3 0-62.2 16.8-78.9 44.4C89 159.9 73.4 152 56 152c-30.9 0-56 25.1-56 56s25.1 56 56 56c11.3 0 21.8-3.3 30.6-9.1c4.9 17.2 14.5 33.1 28.4 45.9L257.2 431.2C269 442 284.4 448 300.4 448H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H352 335.2 320V340.5c0-42.9-28.4-80.5-69.6-92.3l-30.8-8.8c-8.5-2.4-13.4-11.3-11-19.8s11.3-13.4 19.8-11l30.8 8.8c55 15.7 92.8 65.9 92.8 123.1v15.7l56-32.4 6.4-3.7H515.7c33.3 0 60.3-27 60.3-60.3c0-18.1-8.2-35.3-22.2-46.7l-34.6-28.2c-4.6-3.7-9.4-7.1-14.5-10c8.4-49.3-7.8-100-43.9-135.5zm-318.5 382c-14.7 9.8-18.7 29.7-8.9 44.4s29.7 18.7 44.4 8.9l46-30.7-48.6-44.5-32.9 22zM480 240a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"></path></svg></figure><span>Performance monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/hosts" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="server" class="svg-inline--fa fa-server " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"></path></svg></figure><span>Host monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/anomaly-detection" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="siren-on" class="svg-inline--fa fa-siren-on " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M69.3 36l48 32c11 7.4 14 22.3 6.7 33.3s-22.3 14-33.3 6.7l-48-32c-11-7.4-14-22.3-6.7-33.3s22.3-14 33.3-6.7zM597.3 76l-48 32c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l48-32c11-7.4 25.9-4.4 33.3 6.7s4.4 25.9-6.7 33.3zM24 192H88c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24zm528 0h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H552c-13.3 0-24-10.7-24-24s10.7-24 24-24zM144 352l25-199.9c4-32 31.2-56.1 63.5-56.1h175c32.3 0 59.5 24 63.5 56.1L496 352H234.1l21.8-174c1.1-8.8-5.1-16.8-13.9-17.9s-16.8 5.1-17.9 13.9L201.9 352H144zM96 416c0-17.7 14.3-32 32-32H512c17.7 0 32 14.3 32 32v32c0 17.7-14.3 32-32 32H128c-17.7 0-32-14.3-32-32V416z"></path></svg></figure><span>Anomaly detection</span></a></li><li><a href="https://www.appsignal.com/tour/uptime-monitoring" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="traffic-light" class="svg-inline--fa fa-traffic-light " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V352c0 88.4 71.6 160 160 160s160-71.6 160-160V64c0-35.3-28.7-64-64-64H64zm96 416a48 48 0 1 1 0-96 48 48 0 1 1 0 96zm48-176a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zm-48-80a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"></path></svg></figure><span>Uptime monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/metrics" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></figure><span>Metric dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/workflow" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="users" class="svg-inline--fa fa-users " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"></path></svg></figure><span>Workflow</span></a></li><li><a href="https://www.appsignal.com/tour/log-management" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="line-columns" class="svg-inline--fa fa-line-columns " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M224 64c0-17.7-14.3-32-32-32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H192c17.7 0 32-14.3 32-32zm0 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM0 320c0 17.7 14.3 32 32 32H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM224 448c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM288 64c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 192c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32zM288 320c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 448c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32z"></path></svg></figure><span>Log management</span></a></li><li><a href="https://www.appsignal.com/tour/automated-dashboards" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></figure><span>Automated Dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/check-ins" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="square-check" class="svg-inline--fa fa-square-check " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"></path></svg></figure><span>Check-ins</span></a></li><li><a href="https://www.appsignal.com/tour/time-detective" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user-secret" class="svg-inline--fa fa-user-secret " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 16c-6.7 0-10.8-2.8-15.5-6.1C201.9 5.4 194 0 176 0c-30.5 0-52 43.7-66 89.4C62.7 98.1 32 112.2 32 128c0 14.3 25 27.1 64.6 35.9c-.4 4-.6 8-.6 12.1c0 17 3.3 33.2 9.3 48H45.4C38 224 32 230 32 237.4c0 1.7 .3 3.4 1 5l38.8 96.9C28.2 371.8 0 423.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7c0-58.5-28.2-110.4-71.7-143L415 242.4c.6-1.6 1-3.3 1-5c0-7.4-6-13.4-13.4-13.4H342.7c6-14.8 9.3-31 9.3-48c0-4.1-.2-8.1-.6-12.1C391 155.1 416 142.3 416 128c0-15.8-30.7-29.9-78-38.6C324 43.7 302.5 0 272 0c-18 0-25.9 5.4-32.5 9.9c-4.8 3.3-8.8 6.1-15.5 6.1zm56 208H267.6c-16.5 0-31.1-10.6-36.3-26.2c-2.3-7-12.2-7-14.5 0c-5.2 15.6-19.9 26.2-36.3 26.2H168c-22.1 0-40-17.9-40-40V169.6c28.2 4.1 61 6.4 96 6.4s67.8-2.3 96-6.4V184c0 22.1-17.9 40-40 40zm-88 96l16 32L176 480 128 288l64 32zm128-32L272 480 240 352l16-32 64-32z"></path></svg></figure><span>Time Detective</span></a></li></ul></div><div><h3 class="c_h-heading c_h-heading--caps text-sm text-gray-200 pb-3 mb-3 border-b border-gray-700">Supported Languages</h3><ul class="pb-8 space-y-1"><li><a href="https://www.appsignal.com/ruby" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gem" class="svg-inline--fa fa-gem " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M116.7 33.8c4.5-6.1 11.7-9.8 19.3-9.8H376c7.6 0 14.8 3.6 19.3 9.8l112 152c6.8 9.2 6.1 21.9-1.5 30.4l-232 256c-4.5 5-11 7.9-17.8 7.9s-13.2-2.9-17.8-7.9l-232-256c-7.7-8.5-8.3-21.2-1.5-30.4l112-152zm38.5 39.8c-3.3 2.5-4.2 7-2.1 10.5l57.4 95.6L63.3 192c-4.1 .3-7.3 3.8-7.3 8s3.2 7.6 7.3 8l192 16c.4 0 .9 0 1.3 0l192-16c4.1-.3 7.3-3.8 7.3-8s-3.2-7.6-7.3-8L301.5 179.8l57.4-95.6c2.1-3.5 1.2-8.1-2.1-10.5s-7.9-2-10.7 1L256 172.2 165.9 74.6c-2.8-3-7.4-3.4-10.7-1z"></path></svg></figure><span>Ruby (on Rails) APM</span></a></li><li><a href="https://www.appsignal.com/elixir" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="droplet" class="svg-inline--fa fa-droplet " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M192 512C86 512 0 426 0 320C0 228.8 130.2 57.7 166.6 11.7C172.6 4.2 181.5 0 191.1 0h1.8c9.6 0 18.5 4.2 24.5 11.7C253.8 57.7 384 228.8 384 320c0 106-86 192-192 192zM96 336c0-8.8-7.2-16-16-16s-16 7.2-16 16c0 61.9 50.1 112 112 112c8.8 0 16-7.2 16-16s-7.2-16-16-16c-44.2 0-80-35.8-80-80z"></path></svg></figure><span>Elixir APM</span></a></li><li><a href="https://www.appsignal.com/nodejs" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="node-js" class="svg-inline--fa fa-node-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"></path></svg></figure><span>Node.js APM</span></a></li><li><a href="https://www.appsignal.com/javascript" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="square-js" class="svg-inline--fa fa-square-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96zM180.9 444.9c-33.7 0-53.2-17.4-63.2-38.5L152 385.7c6.6 11.7 12.6 21.6 27.1 21.6c13.8 0 22.6-5.4 22.6-26.5V237.7h42.1V381.4c0 43.6-25.6 63.5-62.9 63.5zm85.8-43L301 382.1c9 14.7 20.8 25.6 41.5 25.6c17.4 0 28.6-8.7 28.6-20.8c0-14.4-11.4-19.5-30.7-28l-10.5-4.5c-30.4-12.9-50.5-29.2-50.5-63.5c0-31.6 24.1-55.6 61.6-55.6c26.8 0 46 9.3 59.8 33.7L368 290c-7.2-12.9-15-18-27.1-18c-12.3 0-20.1 7.8-20.1 18c0 12.6 7.8 17.7 25.9 25.6l10.5 4.5c35.8 15.3 55.9 31 55.9 66.2c0 37.8-29.8 58.6-69.7 58.6c-39.1 0-64.4-18.6-76.7-43z"></path></svg></figure><span>JavaScript Error Tracking</span></a></li><li><a href="https://www.appsignal.com/python" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="python" class="svg-inline--fa fa-python " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z"></path></svg></figure><span>Python APM</span></a></li></ul></div><div></div><div></div><div></div><div></div><div><ul><li><a href="https://www.appsignal.com/learning-center" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="book-open" class="svg-inline--fa fa-book-open " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M249.6 471.5c10.8 3.8 22.4-4.1 22.4-15.5V78.6c0-4.2-1.6-8.4-5-11C247.4 52 202.4 32 144 32C93.5 32 46.3 45.3 18.1 56.1C6.8 60.5 0 71.7 0 83.8V454.1c0 11.9 12.8 20.2 24.1 16.5C55.6 460.1 105.5 448 144 448c33.9 0 79 14 105.6 23.5zm76.8 0C353 462 398.1 448 432 448c38.5 0 88.4 12.1 119.9 22.6c11.3 3.8 24.1-4.6 24.1-16.5V83.8c0-12.1-6.8-23.3-18.1-27.6C529.7 45.3 482.5 32 432 32c-58.4 0-103.4 20-123 35.6c-3.3 2.6-5 6.8-5 11V456c0 11.4 11.7 19.3 22.4 15.5z"></path></svg></figure><span>Learn</span></a></li><li><a href="https://docs.appsignal.com" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="book" class="svg-inline--fa fa-book " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M96 0C43 0 0 43 0 96V416c0 53 43 96 96 96H384h32c17.7 0 32-14.3 32-32s-14.3-32-32-32V384c17.7 0 32-14.3 32-32V32c0-17.7-14.3-32-32-32H384 96zm0 384H352v64H96c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8 7.2-16 16-16H336c8.8 0 16 7.2 16 16s-7.2 16-16 16H144c-8.8 0-16-7.2-16-16zm16 48H336c8.8 0 16 7.2 16 16s-7.2 16-16 16H144c-8.8 0-16-7.2-16-16s7.2-16 16-16z"></path></svg></figure><span>Docs</span></a></li><li><a class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base" href="/"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rss" class="svg-inline--fa fa-rss " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 64C0 46.3 14.3 32 32 32c229.8 0 416 186.2 416 416c0 17.7-14.3 32-32 32s-32-14.3-32-32C384 253.6 226.4 96 32 96C14.3 96 0 81.7 0 64zM0 416a64 64 0 1 1 128 0A64 64 0 1 1 0 416zM32 160c159.1 0 288 128.9 288 288c0 17.7-14.3 32-32 32s-32-14.3-32-32c0-123.7-100.3-224-224-224c-17.7 0-32-14.3-32-32s14.3-32 32-32z"></path></svg></figure><span>Blog</span></a></li><li><a href="https://www.appsignal.com/plans" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="dollar-sign" class="svg-inline--fa fa-dollar-sign " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M160 0c17.7 0 32 14.3 32 32V67.7c1.6 .2 3.1 .4 4.7 .7c.4 .1 .7 .1 1.1 .2l48 8.8c17.4 3.2 28.9 19.9 25.7 37.2s-19.9 28.9-37.2 25.7l-47.5-8.7c-31.3-4.6-58.9-1.5-78.3 6.2s-27.2 18.3-29 28.1c-2 10.7-.5 16.7 1.2 20.4c1.8 3.9 5.5 8.3 12.8 13.2c16.3 10.7 41.3 17.7 73.7 26.3l2.9 .8c28.6 7.6 63.6 16.8 89.6 33.8c14.2 9.3 27.6 21.9 35.9 39.5c8.5 17.9 10.3 37.9 6.4 59.2c-6.9 38-33.1 63.4-65.6 76.7c-13.7 5.6-28.6 9.2-44.4 11V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V445.1c-.4-.1-.9-.1-1.3-.2l-.2 0 0 0c-24.4-3.8-64.5-14.3-91.5-26.3c-16.1-7.2-23.4-26.1-16.2-42.2s26.1-23.4 42.2-16.2c20.9 9.3 55.3 18.5 75.2 21.6c31.9 4.7 58.2 2 76-5.3c16.9-6.9 24.6-16.9 26.8-28.9c1.9-10.6 .4-16.7-1.3-20.4c-1.9-4-5.6-8.4-13-13.3c-16.4-10.7-41.5-17.7-74-26.3l-2.8-.7 0 0C119.4 279.3 84.4 270 58.4 253c-14.2-9.3-27.5-22-35.8-39.6c-8.4-17.9-10.1-37.9-6.1-59.2C23.7 116 52.3 91.2 84.8 78.3c13.3-5.3 27.9-8.9 43.2-11V32c0-17.7 14.3-32 32-32z"></path></svg></figure><span>Pricing</span></a></li><li><a href="https://appsignal.com/users/sign_in" class="block mt-4 text-center c-button c-button--sm c-button--gray">Login</a></li><li><a href="https://appsignal.com/users/sign_up" class="block mt-4 text-center c-button c-button--sm">Start free trial</a></li></ul></div></div></nav></header><main><article><header class="bg-purple-800"><div class="c-container"><div class="grid grid-cols-1 items-center gap-12 lg:gap-8 mx-auto py-12 md:py-16 lg:grid-cols-2 max-w-6xl"><div><p class="c_h-heading c_h-heading--caps text-base md:text-lg mb-3 text-purple-200">elixir</p><h1 class="c_h-heading c_h-heading--4xl sm:c_h-heading--5xl md:c_h-heading--6xl text-white mb-6 md:mb-8">Using Dependency Injection in Elixir</h1><div class="flex items-center space-x-2.5"><div class="flex -space-x-2"><figure class="relative z-20 w-6 md:w-8 h-6 md:h-8 rounded-full overflow-hidden border-2 border-white"><img alt="Allan MacGregor" loading="lazy" decoding="async" data-nimg="fill" class="object-cover" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="24px" srcSet="/_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=16&q=90 16w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=32&q=90 32w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=48&q=90 48w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=64&q=90 64w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=96&q=90 96w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=128&q=90 128w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=256&q=90 256w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=384&q=90 384w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=3840&q=90"/></figure></div><div class="flex flex-col"><p class="sm:text-base text-purple-200"><a class="no-underline hover:underline" href="/authors/allan-macgregor.html">Allan MacGregor</a><span class="white-space-nowrap"> <!-- -->on <time dateTime="2024-05-21">May 21, 2024</time></span></p></div></div></div><div class="rounded-lg overflow-hidden border-2 border-white border-opacity-20"><div class="bg-purple-700 relative h-48 sm:h-64 md:h-80"><img alt="Using Dependency Injection in Elixir" decoding="async" data-nimg="fill" class="object-cover" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="(min-width: 1024px) 1200px, 100vw" srcSet="/_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-p1.jpg&w=3840&q=90"/></div></div></div></div></header><div class="c-container pt-16 pb-12"><div class="mb-8 md:mb-10"><div class="md:max-w-[calc(65ch+6rem)] mx-auto p-4 md:px-12 py-6 space-y-4 bg-gray-100 rounded-lg"><h2 class="c_h-heading c_h-heading--xl md:c_h-heading--2xl">This post is part of<!-- --> <a class="no-underline hover:underline" href="/series/dependency-injection-with-exunit-and-rewire-for-elixir.html"><span>Dependency Injection with ExUnit and Rewire for Elixir</span> Series</a></h2><ol class="space-y-3"><li><a class="flex items-center space-x-3 group no-underline" href="/2024/05/21/using-dependency-injection-in-elixir.html"><span class="block shrink-0 w-6 h-6 text-sm text-center text-white leading-6 rounded-lg bg-green-600">1</span><span class="block truncate group-hover:underline font-medium text-green-600">Using Dependency Injection in Elixir</span></a></li><li><a class="flex items-center space-x-3 group no-underline" href="/2024/06/11/advanced-dependency-injection-in-elixir-with-rewire.html"><span class="block shrink-0 w-6 h-6 text-sm text-center text-white leading-6 rounded-lg bg-gray-400">2</span><span class="block truncate group-hover:underline text-gray-700">Advanced Dependency Injection in Elixir with Rewire</span></a></li></ol></div></div><div class="prose md:prose-md group is-breakout mx-auto"><p>While controversial in functional programming, dependency injection can be a useful pattern in Elixir for managing dependencies and improving testability.</p> <p>In this, the first part of a two-part series, we will cover the basic concepts, core principles, and types of dependency injection. We'll explore its benefits in terms of modularity, testability, and maintainability.</p> <p>Then, we will look into a specific scenario where dependency injection can be beneficial, in this case, testing.</p> <p>Let's first explain what dependency injection is.</p> <h2 id="what-is-dependency-injection">What Is Dependency Injection?</h2> <p>Dependency Injection (DI) is a software design pattern that involves supplying an external dependency to a component rather than allowing the component to create the dependency itself. This pattern is a form of Inversion of Control (IoC), where control over the dependencies is inverted from the component to an external entity.</p> <p>The main goal of DI is to reduce coupling between components, making our system more modular, flexible to changes, and easier to test.</p> <p>These are the core concepts of dependency injection:</p> <ul> <li><strong>Dependency</strong>: An entity that another entity depends on to function properly.</li> <li><strong>Injector</strong>: The mechanism that injects dependencies into a component.</li> <li><strong>Client</strong>: The component that depends on the provided dependencies to operate.</li> <li><strong>Service</strong>: The dependency that the client component uses.</li> </ul> <h3 id="types-of-dependency-injection">Types of Dependency Injection</h3> <ol> <li><strong>Constructor Injection</strong>: The dependencies are provided through the component's constructor.</li> <li><strong>Setter Injection</strong>: The dependencies are provided through setter methods or properties.</li> <li><strong>Interface Injection</strong>: The dependency provides an injector method that will inject the dependency into any client passed to it.</li> </ol> <h3 id="advantages-of-dependency-injection">Advantages of Dependency Injection</h3> <ul> <li><strong>Reduced Coupling</strong>: By decoupling components from their dependencies, systems become more modular, allowing for easier maintenance and scalability.</li> <li><strong>Increased Flexibility</strong>: Changing or updating dependencies does not require changes to the dependent components, making the system more adaptable.</li> <li><strong>Improved Testability</strong>: Dependencies can be easily mocked or stubbed in tests, allowing for more isolated and reliable testing.</li> </ul> <h3 id="how-dependency-injection-works">How Dependency Injection Works</h3> <p>There are four steps you should take to leverage dependency injection in your program or service:</p> <ol> <li><strong>Define the Service Interfaces</strong>: These interfaces represent the abstract contracts that services must fulfill.</li> <li><strong>Implement the Services</strong>: Concrete implementations of the service interfaces are developed.</li> <li><strong>Configure the Injector</strong>: The injector is configured to know which service implementations to inject into which clients.</li> <li><strong>Inject Dependencies</strong>: When a client is instantiated, the injector supplies it with the required service implementations based on the configuration.</li> </ol> <h3 id="dependency-injection-diagram">Dependency Injection Diagram</h3> <p>The following diagram illustrates the basic concept of dependency injection:</p> <figure class="mx-auto"><div data-rmiz-wrap="visible" style="display:block"><img alt="Dependency Injection Diagram" loading="lazy" width="1320" height="890" decoding="async" data-nimg="1" style="color:transparent" sizes="sizes="(min-width: 768px) 1600px, 100vw" srcSet="/_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fblog%2F2024-05%2Fdependency-injection-diagram.png&w=3840&q=90"/><button aria-label="Zoom image" data-rmiz-btn-open="true"></button></div></figure> <ul> <li>The <strong>Client</strong> requires a service interface to perform its function.</li> <li>The <strong>Dependency Injector</strong> decides which implementation of the service interface (<code>Service A</code> or <code>Service B</code>) to inject into the client at runtime.</li> <li><strong>Service A</strong> and <strong>Service B</strong> are different implementations of the same service interface. The injector injects one of these into the client based on the configuration or conditions.</li> </ul> <p>This pattern allows for high flexibility and decoupling of components within software applications, facilitating easier management, testing, and evolution of the application code.</p> <h2 id="how-can-dependency-injection-be-applied-in-elixir">How Can Dependency Injection Be Applied in Elixir?</h2> <p>As we mentioned earlier, dependency injection is a pattern that is more commonly associated with object-oriented programming languages. Functional programming languages like Elixir offer a different set of tools and idioms for managing dependencies and state. However, the principles of DI can still be applied in Elixir to achieve similar benefits.</p> <p>In Elixir, the emphasis on explicit over implicit dependency management aligns well with DI principles. For testing purposes, DI allows developers to easily replace real implementations with mocks or stubs, facilitating isolated unit tests that are not dependent on external services or state. This approach enhances test reliability and execution speed, as tests become less brittle and more focused on the functionality being tested.</p> <aside class="not-prose my-8 sm:my-10 md:-mx-12"><div><p class="md:pl-10 mb-4 text-gray-600 leading-none"><span class="w-4 pr-1 text-center">↓</span> Article continues below</p></div><div class="relative overflow-hidden text-white bg-gradient-to-b from-gray-900 to-azure-700 sm:to-gray-900 border border-gray-200 rounded-lg"><div class="flex sm:bg-[radial-gradient(#0A1530, transparent)]"><div class="z-0 relative w-full"><div class="z-0 absolute hidden sm:block bg-cover w-[194px] h-[123px] -right-8 sm:-right-8 md:right-auto md:left-0 top-6 md:top-6"><img role="presentation" alt="Left squiggle" loading="lazy" decoding="async" data-nimg="fill" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="sizes="(min-width: 768px) 1600px, 100vw" srcSet="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&w=3840&q=90"/></div></div><div class="relative min-w-[300px] sm:min-w-[380px] px-4 py-10 mx-auto space-y-4 text-center"><h4 class="z-20 relative c_h-heading c_h-heading--xl sm:c_h-heading--2xl"><span class="block">Is your app broken or slow?</span> <span class="block">AppSignal lets you know.</span></h4><a href="https://www.appsignal.com/elixir" class="z-20 relative c-button c-button--sm c-button--green text-sm">Monitoring by AppSignal →</a></div><div class="z-0 relative w-full"><div class="z-0 absolute hidden sm:block bg-cover w-[168px] h-[136px] -left-8 sm:-left-4 md:left-auto md:right-0 -bottom-14 sm:bottom-0 md:bottom-0"><img role="presentation" alt="Right squiggle" loading="lazy" decoding="async" data-nimg="fill" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="sizes="(min-width: 768px) 1600px, 100vw" srcSet="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&w=3840&q=90"/></div></div></div></div></aside> <h3 id="practical-application-of-dependency-injection-in-elixir-for-testing">Practical Application of Dependency Injection in Elixir for Testing</h3> <p>let's look at how we can use dependency injection to inject mocks and configure dependencies in Elixir.</p> <h4 id="injecting-mocks">Injecting Mocks</h4> <p>One common application of DI in Elixir testing involves injecting mock modules or functions that simulate the behavior of real dependencies. This technique is particularly useful when dealing with external services like databases or APIs.</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-6" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defmodule MyApp.MyModule do def fetch_data(dataSource) do dataSource.query() end end defmodule MyApp.MyModuleTest do use ExUnit.Case test "fetch_data returns expected result" do mockDataSource = %{ query: fn -> {:ok, "mocked data"} end } assert MyApp.MyModule.fetch_data(mockDataSource) == {:ok, "mocked data"} end end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> MyApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">MyModule</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> fetch_data</span><span style="color:#E1E4E8">(dataSource) </span><span style="color:#F97583">do</span></span> <span data-line=""><span style="color:#E1E4E8"> data</span><span style="color:#B392F0">Source</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">query</span><span style="color:#E1E4E8">()</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583">end</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> MyApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">MyModuleTest</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> use</span><span style="color:#B392F0"> ExUnit</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Case</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#E1E4E8"> test </span><span style="color:#9ECBFF">"fetch_data returns expected result"</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#E1E4E8"> mockDataSource </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> %{</span></span> <span data-line=""><span style="color:#79B8FF"> query:</span><span style="color:#F97583"> fn</span><span style="color:#F97583"> -></span><span style="color:#E1E4E8"> {</span><span style="color:#79B8FF">:ok</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">"mocked data"</span><span style="color:#E1E4E8">} </span><span style="color:#F97583">end</span></span> <span data-line=""><span style="color:#E1E4E8"> }</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#E1E4E8"> assert </span><span style="color:#B392F0">MyApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">MyModule</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">fetch_data</span><span style="color:#E1E4E8">(mockDataSource) </span><span style="color:#F97583">==</span><span style="color:#E1E4E8"> {</span><span style="color:#79B8FF">:ok</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">"mocked data"</span><span style="color:#E1E4E8">}</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <p>In this example, <code>MyApp.MyModule.fetch_data/1</code> depends on a <code>dataSource</code> that responds to a <code>query</code> function. During tests, a mock <code>dataSource</code> is injected, allowing the test to run independently of any external data sources.</p> <h4 id="configurable-dependencies">Configurable Dependencies</h4> <p>Another DI strategy involves using application configuration to define dependencies, which can then be overridden in the test environment.</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-6" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="# config/config.exs config :my_app, data_service: MyApp.DataService # config/test.exs config :my_app, data_service: MyApp.MockDataService " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#6A737D"># config/config.exs</span></span> <span data-line=""><span style="color:#E1E4E8">config </span><span style="color:#79B8FF">:my_app</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">data_service:</span><span style="color:#B392F0"> MyApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">DataService</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#6A737D"># config/test.exs</span></span> <span data-line=""><span style="color:#E1E4E8">config </span><span style="color:#79B8FF">:my_app</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">data_service:</span><span style="color:#B392F0"> MyApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">MockDataService</span></span></code></div></pre></div></figure> <p>In your application code, you would fetch the dependency from the application configuration:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-6" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defmodule MyApp.MyModule do def fetch_data do dataSource = Application.get_env(:my_app, :data_service) dataSource.query() end end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> MyApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">MyModule</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> fetch_data</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#E1E4E8"> dataSource </span><span style="color:#F97583">=</span><span style="color:#B392F0"> Application</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">get_env</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">:my_app</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">:data_service</span><span style="color:#E1E4E8">)</span></span> <span data-line=""><span style="color:#E1E4E8"> data</span><span style="color:#B392F0">Source</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">query</span><span style="color:#E1E4E8">()</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <p>This simple example shows how DI can be achieved in Elixir by configuring dependencies at runtime, allowing for easy substitution of real implementations with mocks or stubs during testing.</p> <p>Next, let's review a more practical example that uses DI to inject a mock service into a module for testing.</p> <h2 id="testing-with-dependency-injection">Testing with Dependency Injection</h2> <p>In this example, we will work on <code>EmailScanner</code>, a module that scans emails for spam. We will use a <code>SpamFilterService</code> to check if emails are spam and dependency injection to inject a mock <code>SpamFilterService</code> for testing.</p> <p>Start by creating a new Elixir project:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-6" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Shell</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="bash" data-theme="github-dark" data-raw="mix new email_scanner " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="bash" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#B392F0">mix</span><span style="color:#9ECBFF"> new</span><span style="color:#9ECBFF"> email_scanner</span></span></code></div></pre></div></figure> <p>Now let's move on to implementation and testing.</p> <h3 id="implementation-with-exunit">Implementation with <code>ExUnit</code></h3> <p>First, create the <code>EmailScanner</code> module. This module will depend on a <code>SpamFilterService</code> to check if emails are spam. In this case, the <code>SpamFilterService</code> will be injected as a dependency, making it easy to swap with a mock during testing.</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-6" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defmodule EmailScanner do def scan_email(spam_filter_service, email) do spam_filter_service.check_spam(email) end end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> EmailScanner</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> scan_email</span><span style="color:#E1E4E8">(spam_filter_service, email) </span><span style="color:#F97583">do</span></span> <span data-line=""><span style="color:#E1E4E8"> spam_filter_service.</span><span style="color:#B392F0">check_spam</span><span style="color:#E1E4E8">(email)</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <h3 id="testing-emailscanner-with-exunit">Testing <code>EmailScanner</code> with <code>ExUnit</code></h3> <p>Now, let's write a test for the <code>EmailScanner</code> module using <code>ExUnit</code>. We'll create a mock <code>SpamFilterService</code> to inject during tests:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-6" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defmodule MockSpamFilterService do def check_spam(_email), do: false end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> MockSpamFilterService</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> check_spam</span><span style="color:#E1E4E8">(</span><span style="color:#6A737D">_email</span><span style="color:#E1E4E8">), </span><span style="color:#79B8FF">do:</span><span style="color:#79B8FF"> false</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <p>In this mock, the <code>check_spam/1</code> function always returns <code>false</code>, simulating a non-spam email. Next, let's create a test case that makes use of our new mock:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-6" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defmodule EmailScannerTest do use ExUnit.Case test "scan_email with non-spam email returns false" do non_spam_email = %Email{content: "Hello, world!"} assert false == EmailScanner.scan_email(MockSpamFilterService, non_spam_email) end end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> EmailScannerTest</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> use</span><span style="color:#B392F0"> ExUnit</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Case</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#E1E4E8"> test </span><span style="color:#9ECBFF">"scan_email with non-spam email returns false"</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#E1E4E8"> non_spam_email </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> %</span><span style="color:#B392F0">Email</span><span style="color:#E1E4E8">{</span><span style="color:#79B8FF">content:</span><span style="color:#9ECBFF"> "Hello, world!"</span><span style="color:#E1E4E8">}</span></span> <span data-line=""><span style="color:#E1E4E8"> assert </span><span style="color:#79B8FF">false</span><span style="color:#F97583"> ==</span><span style="color:#B392F0"> EmailScanner</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">scan_email</span><span style="color:#E1E4E8">(</span><span style="color:#B392F0">MockSpamFilterService</span><span style="color:#E1E4E8">, non_spam_email)</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <p>This test injects <code>MockSpamFilterService</code> into <code>EmailScanner</code>, isolating the test from the real spam filtering logic and focusing solely on the <code>EmailScanner</code>'s behavior.</p> <p>By doing this, we can decouple the <code>EmailScanner</code> module from the <code>SpamFilterService</code>, making it easier to test and maintain.</p> <p>Now that we've taken a look at using <code>ExUnit</code> and testing, let's turn to some common dependency injection mistakes to avoid and best practices.</p> <h2 id="common-dependency-injection-pitfalls-and-best-practices">Common Dependency Injection Pitfalls and Best Practices</h2> <p>First, we'll touch on some pitfalls:</p> <ol> <li><strong>Over-Reliance on Mocks</strong>: While DI makes it easy to replace real implementations with mocks, overusing mocks can lead to fragile tests that are overly focused on implementation details rather than behavior.</li> <li><strong>Complex Dependency Graphs</strong>: Introducing DI without careful planning can lead to a tangled web of dependencies that are hard to manage and understand.</li> <li><strong>Ignoring the Complexity of Configuration</strong>: DI often requires some form of configuration to wire up dependencies. This configuration can become complex and unwieldy if not managed properly.</li> </ol> <p>To help you avoid these pitfalls, here are some best practices to follow:</p> <ol> <li><strong>Define Clear Interfaces</strong>: Ensure that your dependencies have clearly defined interfaces.</li> <li><strong>Use Configuration Wisely</strong>: Be mindful of the complexity that configuration can introduce.</li> <li><strong>Leverage Elixir’s Capabilities</strong>: Take advantage of Elixir’s features, such as module attributes and configuration files, to manage your dependencies effectively.</li> <li><strong>Test with Real Implementations When Possible</strong>: While mocking is useful, also test with real implementations to ensure that your system works as expected in real-world scenarios.</li> </ol> <p>That's it for this part of the series!</p> <h2 id="wrapping-up-and-whats-next">Wrapping Up and What's Next</h2> <p>In this article, we have covered the basic concepts of dependency injection, its application in Elixir, and how it can be leveraged for testing. We have also discussed common pitfalls to avoid and best practices to follow when implementing DI in Elixir.</p> <p>In the next and final part of this series, we'll look specifically at advanced dependency injection in Elixir using Rewire.</p> <p>Until then, happy coding!</p> <p><strong>P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, <a href="/elixir-alchemy">subscribe to our Elixir Alchemy newsletter and never miss a single post</a>!</strong></p></div><div><div class="md:max-w-[calc(65ch+6rem)] mx-auto p-4 md:px-12 py-6 space-y-4 bg-gray-100 rounded-lg"><h2 class="c_h-heading c_h-heading--xl md:c_h-heading--2xl">This post is part of<!-- --> <a class="no-underline hover:underline" href="/series/dependency-injection-with-exunit-and-rewire-for-elixir.html"><span>Dependency Injection with ExUnit and Rewire for Elixir</span> Series</a></h2><ol class="space-y-3"><li><a class="flex items-center space-x-3 group no-underline" href="/2024/05/21/using-dependency-injection-in-elixir.html"><span class="block shrink-0 w-6 h-6 text-sm text-center text-white leading-6 rounded-lg bg-green-600">1</span><span class="block truncate group-hover:underline font-medium text-green-600">Using Dependency Injection in Elixir</span></a></li><li><a class="flex items-center space-x-3 group no-underline" href="/2024/06/11/advanced-dependency-injection-in-elixir-with-rewire.html"><span class="block shrink-0 w-6 h-6 text-sm text-center text-white leading-6 rounded-lg bg-gray-400">2</span><span class="block truncate group-hover:underline text-gray-700">Advanced Dependency Injection in Elixir with Rewire</span></a></li></ol></div></div></div></article><section class="c-container || content-visibility-auto" id="authors"><div class="max-w-prose mx-auto py-12 space-y-12 border-b border-gray-200"><section class="flex max-w-prose mx-auto space-x-4 sm:space-x-8"><figure class="relative flex-shrink-0 w-14 h-14"><img alt="Allan MacGregor" loading="lazy" decoding="async" data-nimg="fill" class="object-cover rounded-full" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="56px" srcSet="/_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=16&q=90 16w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=32&q=90 32w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=48&q=90 48w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=64&q=90 64w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=96&q=90 96w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=128&q=90 128w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=256&q=90 256w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=384&q=90 384w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fauthors%2Fallan.jpg&w=3840&q=90"/></figure><div class="space-y-2"><h1 class="c_h-heading c_h-heading--xl leading-tight">Allan MacGregor</h1><div class="space-y-5"><p class="text-gray-700">Guest author Allan is a software engineer and entrepreneur based in Canada. He has 15 years of industry experience, and is passionate about functional programming and Elixir development.</p><span class="flex items-center space-x-2 c-link leading-none"><span class="no-underline"></span><a href="/authors/allan-macgregor.html">All articles by <!-- -->Allan MacGregor</a></span></div></div></section></div><div class="max-w-prose mx-auto flex justify-between items-center pt-12 pb-12 sm:pb-20"><div class="flex items-center space-x-2"><svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M26.6928 5.65956C24.473 5.65956 22.8803 7.6174 21.2452 9.80278C20.224 5.16745 19.0942 0.537415 15.4987 0.537415C11.9031 0.537415 10.7734 5.16745 9.76008 9.79749C8.12501 7.61476 6.53228 5.65427 4.30986 5.65427C2.89439 5.65956 0.5 6.62261 0.5 10.2261L0.515874 16.7822C0.515874 20.3196 2.40493 21.1742 3.5373 21.4811C5.48986 21.989 10.194 22.46 15.4881 22.46C20.7822 22.46 25.489 21.989 27.4415 21.4811C28.5739 21.1847 30.463 20.3196 30.463 16.7822L30.4815 10.2261C30.4815 6.61996 28.1003 5.65956 26.6716 5.65956M15.4987 2.57992C17.655 2.57992 18.4751 6.83162 19.6181 11.93C18.364 13.4724 17.0332 14.7212 15.4987 14.7212C13.9642 14.7212 12.636 13.4724 11.3793 11.93C12.4508 7.15176 13.2763 2.57992 15.4987 2.57992ZM2.55838 16.7822L2.54251 10.2261C2.54251 7.94812 3.77807 7.70207 4.30721 7.70207C5.87614 7.70207 7.55882 10.3187 9.16743 12.4194C8.02976 17.1817 6.8286 20.092 3.99237 19.4888C3.25421 19.2877 2.55838 18.8195 2.55838 16.7822ZM8.11443 20.0973C9.36851 18.7321 10.1702 16.605 10.7813 14.3508C12.1042 15.7239 13.6096 16.7637 15.4881 16.7637C17.3666 16.7637 18.8826 15.7239 20.1975 14.3508C20.806 16.6023 21.605 18.7321 22.8618 20.0973C20.6684 20.2984 18.0254 20.4175 15.4881 20.4175C12.9508 20.4175 10.3077 20.2984 8.11443 20.0973ZM28.439 16.7822C28.439 18.8195 27.7431 19.2877 27.005 19.4888C24.1661 20.0894 22.9676 17.1844 21.8326 12.4194C23.4385 10.3266 25.1239 7.70207 26.6928 7.70207C27.2219 7.70207 28.4575 7.94812 28.4575 10.2261L28.439 16.7822Z" fill="#415377"></path></svg><p class="text-lg font-semibold leading-none">Become our next author!</p></div><a class="c-button c-button--white c-button--xs space-x-1.5 text-sm" href="/write-for-us.html"><span>Find out more</span></a></div></section><section class="relative flex px-4 sm:px-0 bg-gray-100 || content-visibility-auto"><div class="absolute h-1/3 sm:h-1/3 inset-x-0 inset-y-0 bg-white"></div><div class="z-10 w-full max-w-4xl sm:px-8 lg:px-0 mx-auto"><aside class="sm:flex space-y-6 sm:space-y-0 mx-auto px-4 sm:px-8 md:px-16 py-6 sm:py-8 md:py-12 bg-purple-700 text-white rounded-lg"><div class="sm:pr-16"><div class="w-28 sm:w-32"><img alt="Newsletter Logo" loading="lazy" width="206" height="106" decoding="async" data-nimg="1" style="color:transparent" src="/images/newsletter/logo-elixir.svg"/></div></div><div class="space-y-8"><h1 class="c_h-heading c_h-heading--2xl sm:c_h-heading--3xl">Subscribe to our Elixir Alchemy email series and receive deep insights about Elixir, Phoenix and other developments.</h1><form name="mc-embedded-subscribe-form" action="https://appsignal.us3.list-manage.com/subscribe/post?u=6a5bb0e75a9d07c60a71c7a9f&id=de0b0bb410" method="post"><div class="space-y-3 lg:space-y-0 lg:flex lg:space-x-3"><div class="space-y-3 md:space-y-0 pb-1 md:pb-0 md:flex md:space-x-2 w-full"><div class="w-full flex-grow"><input placeholder="Email" required="" type="email" class="c-form__input px-3 py-2" name="EMAIL"/></div><div class="w-full flex-grow"><input placeholder="First name" required="" type="text" class="c-form__input px-3 py-2" name="NAME"/></div></div><button class="c-button w-full lg:w-auto flex-none" type="submit" name="subscribe"><span class="text-center mx-auto">Subscribe</span></button></div><div aria-hidden="true" style="position:absolute;left:-5000px"><input tabindex="-1" type="text" name="b_6a5bb0e75a9d07c60a71c7a9f_de0b0bb410"/></div></form></div></aside></div></section><div class="pb-12 sm:pb-24 bg-gray-100"></div><section class="bg-gradient-to-b from-gray-900 to-blue-800 || content-visibility-auto"><div class="c-container max-w-7xl text-center pt-20 sm:pt-24 overflow-hidden"><div class="max-w-3xl mx-auto space-y-4 sm:space-y-5"><h1 class="c_h-heading c_h-heading--4xl sm:c_h-heading--5xl text-white">AppSignal monitors your apps</h1><p class="text-lg lg:text-xl text-gray-200">AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love<!-- --> <a href="https://www.appsignal.com/waffles" target="_blank" rel="noopener noreferrer">stroopwafels</a>. If you do too,<!-- --> <a href="mailto:contact@appsignal.com">let us know</a>. We might send you some!</p></div><a href="https://www.appsignal.com" class="mt-10 c-button c-button--md c-button--green">Discover AppSignal</a><div class="relative mt-16"><div class="max-h-40 max-w-md mx-auto || sm:hidden"><img alt="AppSignal monitors your apps" loading="lazy" width="552" height="320" decoding="async" data-nimg="1" style="color:transparent" sizes="sizes="(min-width: 768px) 1600px, 100vw" srcSet="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&w=3840&q=90"/></div><div class="max-h-64 md:max-h-80 lg:max-h-100 lg:mb-4 || hidden sm:block"><img alt="AppSignal monitors your apps" loading="lazy" width="1280" height="720" decoding="async" data-nimg="1" style="color:transparent" sizes="sizes="(min-width: 768px) 1600px, 100vw" srcSet="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=640&q=90 640w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=750&q=90 750w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=828&q=90 828w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=1080&q=90 1080w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=1200&q=90 1200w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=1920&q=90 1920w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=2048&q=90 2048w, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=3840&q=90 3840w" src="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&w=3840&q=90"/></div></div></div></section></main><footer class="relative px-4 py-16 sm:pt-20 xl:pb-28 dark text-gray-300 bg-gray-900"><div class="c-container"><div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-10 gap-x-8 gap-y-10"><div class="col-span-1 sm:col-span-2 xl:col-span-2"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Features</h2><ul class="space-y-2 sm:columns-2 xl:columns-1"><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"></path></svg></span><div><a href="https://www.appsignal.com/tour/errors/" class="no-underline text-white undefined sm:text-lg hover:underline">Error tracking</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rabbit-running" class="svg-inline--fa fa-rabbit-running " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M460.7 39.3l-2.2-2.1c-4.7-4.6-11.5-6.4-17.9-4.7s-11.4 6.6-13.1 13l-.8 2.9c-3.3 11.8-5.1 23.7-5.5 35.6c24.3 20.6 42.9 47.8 53.3 78.8c-8.2-1.9-16.5-2.9-25-2.9c-.6 0-1.1 0-1.7 0c-18.4-44-56.5-77.4-103.6-89.3l-3.4-.9c-6.2-1.6-12.9-.6-18.5 2.6c-10.8 6.2-15.6 19.5-11.2 31.1c14.9 39.5 44.3 71.4 81.4 89.7c-3.7 6.6-6.4 14-7.6 21.8L279.7 154.9C248.8 137.3 213.8 128 178.2 128c-32.3 0-62.2 16.8-78.9 44.4C89 159.9 73.4 152 56 152c-30.9 0-56 25.1-56 56s25.1 56 56 56c11.3 0 21.8-3.3 30.6-9.1c4.9 17.2 14.5 33.1 28.4 45.9L257.2 431.2C269 442 284.4 448 300.4 448H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H352 335.2 320V340.5c0-42.9-28.4-80.5-69.6-92.3l-30.8-8.8c-8.5-2.4-13.4-11.3-11-19.8s11.3-13.4 19.8-11l30.8 8.8c55 15.7 92.8 65.9 92.8 123.1v15.7l56-32.4 6.4-3.7H515.7c33.3 0 60.3-27 60.3-60.3c0-18.1-8.2-35.3-22.2-46.7l-34.6-28.2c-4.6-3.7-9.4-7.1-14.5-10c8.4-49.3-7.8-100-43.9-135.5zm-318.5 382c-14.7 9.8-18.7 29.7-8.9 44.4s29.7 18.7 44.4 8.9l46-30.7-48.6-44.5-32.9 22zM480 240a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/performance" class="no-underline text-white undefined sm:text-lg hover:underline">Performance monitoring</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="server" class="svg-inline--fa fa-server " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/hosts" class="no-underline text-white undefined sm:text-lg hover:underline">Host monitoring</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="siren-on" class="svg-inline--fa fa-siren-on " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M69.3 36l48 32c11 7.4 14 22.3 6.7 33.3s-22.3 14-33.3 6.7l-48-32c-11-7.4-14-22.3-6.7-33.3s22.3-14 33.3-6.7zM597.3 76l-48 32c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l48-32c11-7.4 25.9-4.4 33.3 6.7s4.4 25.9-6.7 33.3zM24 192H88c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24zm528 0h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H552c-13.3 0-24-10.7-24-24s10.7-24 24-24zM144 352l25-199.9c4-32 31.2-56.1 63.5-56.1h175c32.3 0 59.5 24 63.5 56.1L496 352H234.1l21.8-174c1.1-8.8-5.1-16.8-13.9-17.9s-16.8 5.1-17.9 13.9L201.9 352H144zM96 416c0-17.7 14.3-32 32-32H512c17.7 0 32 14.3 32 32v32c0 17.7-14.3 32-32 32H128c-17.7 0-32-14.3-32-32V416z"></path></svg></span><div><a href="https://www.appsignal.com/tour/anomaly-detection" class="no-underline text-white undefined sm:text-lg hover:underline">Anomaly detection</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="traffic-light" class="svg-inline--fa fa-traffic-light " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V352c0 88.4 71.6 160 160 160s160-71.6 160-160V64c0-35.3-28.7-64-64-64H64zm96 416a48 48 0 1 1 0-96 48 48 0 1 1 0 96zm48-176a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zm-48-80a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"></path></svg></span><div><a href="https://www.appsignal.com/tour/uptime-monitoring" class="no-underline text-white undefined sm:text-lg hover:underline">Uptime monitoring</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/metrics" class="no-underline text-white undefined sm:text-lg hover:underline">Metric dashboards</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="users" class="svg-inline--fa fa-users " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"></path></svg></span><div><a href="https://www.appsignal.com/tour/workflow" class="no-underline text-white undefined sm:text-lg hover:underline">Workflow</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="line-columns" class="svg-inline--fa fa-line-columns " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M224 64c0-17.7-14.3-32-32-32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H192c17.7 0 32-14.3 32-32zm0 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM0 320c0 17.7 14.3 32 32 32H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM224 448c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM288 64c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 192c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32zM288 320c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 448c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32z"></path></svg></span><div><a href="https://www.appsignal.com/tour/log-management" class="no-underline text-white undefined sm:text-lg hover:underline">Log management</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/automated-dashboards" class="no-underline text-white undefined sm:text-lg hover:underline">Automated Dashboards</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="square-check" class="svg-inline--fa fa-square-check " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"></path></svg></span><div><a href="https://www.appsignal.com/tour/check-ins" class="no-underline text-white undefined sm:text-lg hover:underline">Check-ins</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user-secret" class="svg-inline--fa fa-user-secret " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 16c-6.7 0-10.8-2.8-15.5-6.1C201.9 5.4 194 0 176 0c-30.5 0-52 43.7-66 89.4C62.7 98.1 32 112.2 32 128c0 14.3 25 27.1 64.6 35.9c-.4 4-.6 8-.6 12.1c0 17 3.3 33.2 9.3 48H45.4C38 224 32 230 32 237.4c0 1.7 .3 3.4 1 5l38.8 96.9C28.2 371.8 0 423.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7c0-58.5-28.2-110.4-71.7-143L415 242.4c.6-1.6 1-3.3 1-5c0-7.4-6-13.4-13.4-13.4H342.7c6-14.8 9.3-31 9.3-48c0-4.1-.2-8.1-.6-12.1C391 155.1 416 142.3 416 128c0-15.8-30.7-29.9-78-38.6C324 43.7 302.5 0 272 0c-18 0-25.9 5.4-32.5 9.9c-4.8 3.3-8.8 6.1-15.5 6.1zm56 208H267.6c-16.5 0-31.1-10.6-36.3-26.2c-2.3-7-12.2-7-14.5 0c-5.2 15.6-19.9 26.2-36.3 26.2H168c-22.1 0-40-17.9-40-40V169.6c28.2 4.1 61 6.4 96 6.4s67.8-2.3 96-6.4V184c0 22.1-17.9 40-40 40zm-88 96l16 32L176 480 128 288l64 32zm128-32L272 480 240 352l16-32 64-32z"></path></svg></span><div><a href="https://www.appsignal.com/tour/time-detective" class="no-underline text-white undefined sm:text-lg hover:underline">Time Detective</a></div></div></li></ul></div><div class="col-span-1 xl:col-span-2"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Resources</h2><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/plans" class="no-underline text-white undefined sm:text-lg hover:underline">Plans & pricing</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://docs.appsignal.com/" class="no-underline text-white undefined sm:text-lg hover:underline">Documentation</a></div></div></li><li><div class="flex items-center space-x-3"><div><a class="no-underline text-white undefined sm:text-lg hover:underline" href="/">Blog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/customers" class="no-underline text-white undefined sm:text-lg hover:underline">Customer Stories</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/changelog" class="no-underline text-white undefined sm:text-lg hover:underline">Changelog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/learning-center" class="no-underline text-white undefined sm:text-lg hover:underline">Learning Center</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/why" class="no-underline text-white undefined sm:text-lg hover:underline">Why AppSignal</a></div></div></li></ul></div><div class="col-span-1 xl:col-span-2"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Compare</h2><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/new-relic-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">vs New Relic</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/datadog-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">vs Datadog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/sentry-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">vs Sentry</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/scout-apm-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">vs Scout APM</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/honeybadger-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">vs Honeybadger</a></div></div></li></ul></div><div class="col-span-1 xl:col-span-2"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Support</h2><p class="text-sm mb-4">Do you need help, have a feature request or just need someone to rubber duck with? Get in touch with one of our engineers.</p><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/contact" class="no-underline text-white undefined sm:text-lg hover:underline">Contact us</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="mailto:support@appsignal.com" class="no-underline text-white undefined sm:text-lg hover:underline">Live chat</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://status.appsignal.com/" class="no-underline text-white undefined sm:text-lg hover:underline">Status</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/security" class="no-underline text-white undefined sm:text-lg hover:underline">Security</a></div></div></li></ul></div><div class="col-span-1 xl:col-span-2"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">About us</h2><p class="text-sm mb-4"><span>AppSignal is located in the beautiful Netherlands. We love<!-- --> <a href="https://www.appsignal.com/waffles">stroopwafels</a>. If you do too, <a href="mailto:support@appsignal.com">let us know</a>. We might send you some!</span></p><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/about" class="no-underline text-white undefined sm:text-lg hover:underline">About</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/jobs" class="no-underline text-white undefined sm:text-lg hover:underline">Jobs</a></div></div></li><li><div class="flex items-center space-x-3"><div><a class="no-underline text-white undefined sm:text-lg hover:underline" href="/write-for-us">Write for Our Blog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/diversity" class="no-underline text-white undefined sm:text-lg hover:underline">Diversity</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/open-source" class="no-underline text-white undefined sm:text-lg hover:underline">Open Source</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://x.com/appsignal" class="no-underline text-white undefined sm:text-lg hover:underline">X</a></div></div></li></ul></div><div class="col-span-1 sm:col-span-2 xl:col-span-10"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Languages</h2><div class="grid grid-cols-1 sm:grid-cols-3 xl:grid-cols-5 gap-6 sm:gap-12"><div class="order-1 sm:order-1 xl:order-1"><ul class="space-y-2 order-1 sm:order-1 xl:order-1"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gem" class="svg-inline--fa fa-gem " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M116.7 33.8c4.5-6.1 11.7-9.8 19.3-9.8H376c7.6 0 14.8 3.6 19.3 9.8l112 152c6.8 9.2 6.1 21.9-1.5 30.4l-232 256c-4.5 5-11 7.9-17.8 7.9s-13.2-2.9-17.8-7.9l-232-256c-7.7-8.5-8.3-21.2-1.5-30.4l112-152zm38.5 39.8c-3.3 2.5-4.2 7-2.1 10.5l57.4 95.6L63.3 192c-4.1 .3-7.3 3.8-7.3 8s3.2 7.6 7.3 8l192 16c.4 0 .9 0 1.3 0l192-16c4.1-.3 7.3-3.8 7.3-8s-3.2-7.6-7.3-8L301.5 179.8l57.4-95.6c2.1-3.5 1.2-8.1-2.1-10.5s-7.9-2-10.7 1L256 172.2 165.9 74.6c-2.8-3-7.4-3.4-10.7-1z"></path></svg></span><div><a href="https://www.appsignal.com/ruby" class="no-underline text-white text-lg sm:text-lg hover:underline">Ruby</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/ruby/active-record-monitoring" class="no-underline hover:underline">Active Record</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/capistrano-monitoring" class="no-underline hover:underline">Capistrano</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/delayed-job-monitoring" class="no-underline hover:underline">Delayed::Job</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/garbage-collection-monitoring" class="no-underline hover:underline">Garbage Collection</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/global-vm-lock-monitoring" class="no-underline hover:underline">Global VM Lock</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/grape-monitoring" class="no-underline hover:underline">Grape</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/graphql-monitoring" class="no-underline hover:underline">GraphQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/hanami-monitoring" class="no-underline hover:underline">Hanami</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/mongodb-monitoring" class="no-underline hover:underline">MongoDB</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/padrino-monitoring" class="no-underline hover:underline">Padrino</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/puma-monitoring" class="no-underline hover:underline">Puma</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/que-monitoring" class="no-underline hover:underline">Que</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/rails-monitoring" class="no-underline hover:underline">Ruby on Rails</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/rake-monitoring" class="no-underline hover:underline">Rake</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/resque-monitoring" class="no-underline hover:underline">Resque</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/shoryuken-monitoring" class="no-underline hover:underline">Shoryuken</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/sidekiq-monitoring" class="no-underline hover:underline">Sidekiq</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/sinatra-monitoring" class="no-underline hover:underline">Sinatra</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/solid-queue-monitoring" class="no-underline hover:underline">Solid Queue</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/view-component-monitoring" class="no-underline hover:underline">ViewComponent</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/webmachine-monitoring" class="no-underline hover:underline">Webmachine</a><span class="sm:hidden"></span></span></p></li></ul></div><div class="order-2 sm:order-4 xl:order-2"><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="droplet" class="svg-inline--fa fa-droplet " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M192 512C86 512 0 426 0 320C0 228.8 130.2 57.7 166.6 11.7C172.6 4.2 181.5 0 191.1 0h1.8c9.6 0 18.5 4.2 24.5 11.7C253.8 57.7 384 228.8 384 320c0 106-86 192-192 192zM96 336c0-8.8-7.2-16-16-16s-16 7.2-16 16c0 61.9 50.1 112 112 112c8.8 0 16-7.2 16-16s-7.2-16-16-16c-44.2 0-80-35.8-80-80z"></path></svg></span><div><a href="https://www.appsignal.com/elixir" class="no-underline text-white text-lg sm:text-lg hover:underline">Elixir</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/elixir/absinthe-monitoring" class="no-underline hover:underline">Absinthe</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/ecto-monitoring" class="no-underline hover:underline">Ecto</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/erlang-monitoring" class="no-underline hover:underline">Erlang</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/finch-monitoring" class="no-underline hover:underline">Finch</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/oban-monitoring" class="no-underline hover:underline">Oban</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/phoenix-monitoring" class="no-underline hover:underline">Phoenix</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/plug-monitoring" class="no-underline hover:underline">Plug</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/tesla-monitoring" class="no-underline hover:underline">Tesla</a><span class="sm:hidden"></span></span></p></li></ul></div><div class="order-3 sm:order-2 xl:order-3"><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="node-js" class="svg-inline--fa fa-node-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"></path></svg></span><div><a href="https://www.appsignal.com/nodejs" class="no-underline text-white text-lg sm:text-lg hover:underline">Node.js</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/nodejs/amqplib-monitoring" class="no-underline hover:underline">AMQPlib</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/apollo-gateway-monitoring" class="no-underline hover:underline">Apollo Gateway</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/bullmq-monitoring" class="no-underline hover:underline">BullMQ</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/express-monitoring" class="no-underline hover:underline">Express</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/fastify-monitoring" class="no-underline hover:underline">Fastify</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/graphql-monitoring" class="no-underline hover:underline">GraphQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/knexjs-monitoring" class="no-underline hover:underline">Knex.js</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/koa-monitoring" class="no-underline hover:underline">Koa</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/mongodb-monitoring" class="no-underline hover:underline">MongoDB</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/mongoose-monitoring" class="no-underline hover:underline">Mongoose</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/mysql-monitoring" class="no-underline hover:underline">MySQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/nestjs-monitoring" class="no-underline hover:underline">NestJS</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/nextjs-monitoring" class="no-underline hover:underline">Next.js</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/pino-monitoring" class="no-underline hover:underline">Pino</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/postgresql-monitoring" class="no-underline hover:underline">PostgreSQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/prisma-monitoring" class="no-underline hover:underline">Prisma</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/redis-monitoring" class="no-underline hover:underline">Redis</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/remix-monitoring" class="no-underline hover:underline">Remix</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/restify-monitoring" class="no-underline hover:underline">Restify</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/winston-monitoring" class="no-underline hover:underline">Winston</a><span class="sm:hidden"></span></span></p></li></ul></div><div class="order-4 sm:order-5 xl:order-4"><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="square-js" class="svg-inline--fa fa-square-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96zM180.9 444.9c-33.7 0-53.2-17.4-63.2-38.5L152 385.7c6.6 11.7 12.6 21.6 27.1 21.6c13.8 0 22.6-5.4 22.6-26.5V237.7h42.1V381.4c0 43.6-25.6 63.5-62.9 63.5zm85.8-43L301 382.1c9 14.7 20.8 25.6 41.5 25.6c17.4 0 28.6-8.7 28.6-20.8c0-14.4-11.4-19.5-30.7-28l-10.5-4.5c-30.4-12.9-50.5-29.2-50.5-63.5c0-31.6 24.1-55.6 61.6-55.6c26.8 0 46 9.3 59.8 33.7L368 290c-7.2-12.9-15-18-27.1-18c-12.3 0-20.1 7.8-20.1 18c0 12.6 7.8 17.7 25.9 25.6l10.5 4.5c35.8 15.3 55.9 31 55.9 66.2c0 37.8-29.8 58.6-69.7 58.6c-39.1 0-64.4-18.6-76.7-43z"></path></svg></span><div><a href="https://www.appsignal.com/javascript" class="no-underline text-white text-lg sm:text-lg hover:underline">JavaScript</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/javascript/react-monitoring" class="no-underline hover:underline">React</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/vue-monitoring" class="no-underline hover:underline">Vue</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/angular-monitoring" class="no-underline hover:underline">Angular</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/ember-monitoring" class="no-underline hover:underline">Ember</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/preact-monitoring" class="no-underline hover:underline">Preact</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/stimulus-monitoring" class="no-underline hover:underline">Stimulus</a><span class="sm:hidden"></span></span></p></li></ul></div><div class="order-5 sm:order-3 xl:order-5"><ul class="space-y-2 undefined"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="python" class="svg-inline--fa fa-python " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z"></path></svg></span><div><a href="https://www.appsignal.com/python" class="no-underline text-white text-lg sm:text-lg hover:underline">Python</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/python/celery-monitoring" class="no-underline hover:underline">Celery</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/django-monitoring" class="no-underline hover:underline">Django</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/fastapi-monitoring" class="no-underline hover:underline">FastAPI</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/flask-monitoring" class="no-underline hover:underline">Flask</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/jinja2-monitoring" class="no-underline hover:underline">Jinja2</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/mysql-monitoring" class="no-underline hover:underline">MySQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/pika-monitoring" class="no-underline hover:underline">Pika</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/postgresql-monitoring" class="no-underline hover:underline">PostgreSQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/redis-monitoring" class="no-underline hover:underline">Redis</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/request-monitoring" class="no-underline hover:underline">Request</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/sqlalchemy-monitoring" class="no-underline hover:underline">SQLAlchemy</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/sqlite-monitoring" class="no-underline hover:underline">SQLite</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/starlette-monitoring" class="no-underline hover:underline">Starlette</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/wsgi-asgi-monitoring" class="no-underline hover:underline">WSGI and ASGI</a><span class="sm:hidden"></span></span></p></li></ul></div></div></div><div class="col-span-1 sm:col-span-2 xl:col-span-10 border-t border-gray-800 pt-6 xl:flex xl:items-center xl:justify-between"><div class="w-[168px] h-[31px] mb-4 xl:mb-0"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350.92 64.54"><g fill="#fff"><path d="M341.56 12.58c-5.46 0-9.37 4.82-13.39 10.18C325.68 11.38 322.9 0 314.07 0S302.46 11.38 300 22.76c-4-5.36-7.93-10.18-13.39-10.18-3.47 0-9.35 2.37-9.35 11.22v16.11c0 8.69 4.64 10.82 7.42 11.54 4.8 1.25 16.36 2.4 29.37 2.4s24.57-1.15 29.36-2.4c2.79-.72 7.43-2.85 7.43-11.54V23.8c0-8.85-5.88-11.22-9.36-11.22M314.07 5c5.29 0 7.3 10.44 10.12 23-3.09 3.79-6.36 6.86-10.12 6.86S307 31.78 304 28c2.63-11.72 4.65-23 10.12-23m-31.84 34.91V23.8c0-5.59 3-6.2 4.33-6.2 3.85 0 8 6.43 11.94 11.59-2.83 11.71-5.78 18.81-12.75 17.37-1.82-.5-3.52-1.65-3.52-6.65M296 48.05c3-3.35 5-8.58 6.5-14.12 3.23 3.38 6.95 5.93 11.57 5.93s8.33-2.55 11.56-5.93c1.5 5.54 3.47 10.77 6.55 14.12-5.39.5-11.88.79-18.11.79s-12.73-.29-18.12-.79m49.91-8.14c0 5-1.71 6.15-3.52 6.65-7 1.48-9.93-5.66-12.72-17.37 3.95-5.16 8.09-11.59 11.94-11.59 1.3 0 4.34.61 4.34 6.2ZM1.13 52.07a1.12 1.12 0 0 1-.79-.34 1.16 1.16 0 0 1-.34-.85 1.52 1.52 0 0 1 .11-.52L14 13.74a2 2 0 0 1 .62-1 1.81 1.81 0 0 1 1.25-.4h5a1.81 1.81 0 0 1 1.25.4 2.44 2.44 0 0 1 .68 1l13.82 36.62a2.52 2.52 0 0 1 .06.52 1.16 1.16 0 0 1-.34.85 1.12 1.12 0 0 1-.79.34h-3.92a1.4 1.4 0 0 1-1.07-.37 2.41 2.41 0 0 1-.46-.7l-2.89-7.48H9.47L6.63 51a2 2 0 0 1-.48.66 1.5 1.5 0 0 1-1.1.37Zm10.15-14.18H25.4l-7.09-18.82ZM42.52 62.84a1.25 1.25 0 0 1-1.3-1.31V23.89a1.24 1.24 0 0 1 1.3-1.3h3.57a1.25 1.25 0 0 1 1.31 1.3v2.38a11.6 11.6 0 0 1 3.62-3A11.35 11.35 0 0 1 56.58 22a12.67 12.67 0 0 1 5.33 1 10.13 10.13 0 0 1 3.68 2.83 12.56 12.56 0 0 1 2.24 4.26 19.79 19.79 0 0 1 .88 5.21c0 .64.06 1.31.06 2s0 1.34-.06 2a18.1 18.1 0 0 1-.82 5.16 13.14 13.14 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.56 12.56 0 0 1-5.36 1.05 11.36 11.36 0 0 1-5.36-1.16 10.55 10.55 0 0 1-3.54-3v13a1.27 1.27 0 0 1-.37.94 1.38 1.38 0 0 1-1 .37ZM55 47.3a6.63 6.63 0 0 0 4.17-1.16 6.53 6.53 0 0 0 2.18-3 14.43 14.43 0 0 0 .8-4.14 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.8-4.14 6.58 6.58 0 0 0-2.18-3A6.69 6.69 0 0 0 55 27.35a6.44 6.44 0 0 0-6.49 4.22 12.43 12.43 0 0 0-.82 3.77q-.06 1-.06 2.16c0 .79 0 1.53.06 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86A6.83 6.83 0 0 0 55 47.3ZM75.71 62.84a1.27 1.27 0 0 1-1.3-1.31V23.89a1.25 1.25 0 0 1 1.3-1.3h3.58a1.29 1.29 0 0 1 .93.36 1.27 1.27 0 0 1 .37.94v2.38a11.53 11.53 0 0 1 3.63-3A11.29 11.29 0 0 1 89.77 22a12.64 12.64 0 0 1 5.33 1 10.16 10.16 0 0 1 3.69 2.83 12.74 12.74 0 0 1 2.21 4.3 20.21 20.21 0 0 1 .88 5.21v4a18.1 18.1 0 0 1-.83 5.16 12.94 12.94 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.53 12.53 0 0 1-5.36 1.05 11.31 11.31 0 0 1-5.35-1.16 10.49 10.49 0 0 1-3.55-3v13a1.27 1.27 0 0 1-.37.94 1.34 1.34 0 0 1-1 .37ZM88.19 47.3a6.1 6.1 0 0 0 6.35-4.19 14.43 14.43 0 0 0 .79-4.11 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.79-4.14 6.1 6.1 0 0 0-6.35-4.19 6.44 6.44 0 0 0-6.5 4.22 12.74 12.74 0 0 0-.82 3.77c0 .64-.05 1.36-.05 2.16s0 1.53.05 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86 6.84 6.84 0 0 0 4.09 1.1ZM122.17 52.63a21.18 21.18 0 0 1-8.56-1.53 12.82 12.82 0 0 1-5.27-4 9.44 9.44 0 0 1-1.93-5.25 1.1 1.1 0 0 1 .34-.79 1.15 1.15 0 0 1 .85-.34h4a1.47 1.47 0 0 1 1 .31 2.75 2.75 0 0 1 .57.76 6.36 6.36 0 0 0 1.36 2.39 7.72 7.72 0 0 0 2.86 2 12.06 12.06 0 0 0 4.73.79q4.48 0 6.61-1.53a4.88 4.88 0 0 0 2.12-4.19 3.83 3.83 0 0 0-1.13-2.87 10.56 10.56 0 0 0-3.52-2c-1.58-.6-3.64-1.25-6.17-1.93a36.52 36.52 0 0 1-7-2.55 10.29 10.29 0 0 1-4.19-3.57 9.81 9.81 0 0 1-1.39-5.44 9.72 9.72 0 0 1 1.73-5.67 11.66 11.66 0 0 1 4.93-3.94 18.66 18.66 0 0 1 7.68-1.45 19 19 0 0 1 6.35 1 14 14 0 0 1 4.57 2.55 11.53 11.53 0 0 1 2.77 3.4 8.31 8.31 0 0 1 1 3.57 1.18 1.18 0 0 1-.32.77 1.09 1.09 0 0 1-.87.37h-4.14a1.83 1.83 0 0 1-.88-.23 1.5 1.5 0 0 1-.65-.85 5.22 5.22 0 0 0-2.44-3.46 9.49 9.49 0 0 0-5.39-1.42 10.19 10.19 0 0 0-5.41 1.28 4.35 4.35 0 0 0-2.07 4 4.24 4.24 0 0 0 1 2.89 8.38 8.38 0 0 0 3.18 2 58.53 58.53 0 0 0 5.75 1.9 43.26 43.26 0 0 1 7.76 2.54 10.38 10.38 0 0 1 4.4 3.51 9.64 9.64 0 0 1 1.41 5.42 10 10 0 0 1-1.95 6.2 12.32 12.32 0 0 1-5.44 4 22 22 0 0 1-8.25 1.36ZM143.77 17.37a1.34 1.34 0 0 1-1-.37 1.24 1.24 0 0 1-.37-.93v-3.58a1.34 1.34 0 0 1 .37-1 1.32 1.32 0 0 1 1-.4h4.31a1.39 1.39 0 0 1 1 .4 1.29 1.29 0 0 1 .4 1v3.58a1.2 1.2 0 0 1-.4.93 1.41 1.41 0 0 1-1 .37Zm.34 34.7a1.38 1.38 0 0 1-1-.37 1.27 1.27 0 0 1-.37-.94V23.89a1.27 1.27 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.69a1.25 1.25 0 0 1 1.3 1.3v26.87a1.27 1.27 0 0 1-.37.94 1.28 1.28 0 0 1-.93.37ZM168.5 64.54a17.5 17.5 0 0 1-6.58-1.05 12.16 12.16 0 0 1-4-2.52 9.27 9.27 0 0 1-2.09-2.97 8.1 8.1 0 0 1-.65-2.38 1.13 1.13 0 0 1 .34-1 1.3 1.3 0 0 1 1-.39h3.86a1.51 1.51 0 0 1 .85.22 1.8 1.8 0 0 1 .56 1 11.19 11.19 0 0 0 1 1.73 5 5 0 0 0 1.93 1.61 7.69 7.69 0 0 0 3.57.68 11 11 0 0 0 4.05-.65 4.82 4.82 0 0 0 2.5-2.3 9.59 9.59 0 0 0 .87-4.47v-3.84a10.93 10.93 0 0 1-3.64 2.79 11.93 11.93 0 0 1-5.38 1.08 12.69 12.69 0 0 1-5.36-1 9.78 9.78 0 0 1-3.69-2.86 13.36 13.36 0 0 1-2.21-4.28 18.82 18.82 0 0 1-.82-5.27q-.06-1.53 0-3.12a18.94 18.94 0 0 1 .8-5.16 12.85 12.85 0 0 1 2.21-4.39 10.34 10.34 0 0 1 3.71-2.94 12.23 12.23 0 0 1 5.36-1.06 11.1 11.1 0 0 1 5.52 1.25 11.77 11.77 0 0 1 3.66 3.06V24a1.37 1.37 0 0 1 .37-1 1.31 1.31 0 0 1 1-.39h3.57a1.33 1.33 0 0 1 1.35 1.39v27.39a16.14 16.14 0 0 1-1.38 6.91 10 10 0 0 1-4.4 4.59 16.32 16.32 0 0 1-7.88 1.65Zm-.17-17.69a6.62 6.62 0 0 0 4.11-1.19 7.39 7.39 0 0 0 2.33-3 10.87 10.87 0 0 0 .87-3.71c0-.49.06-1.12.06-1.9s0-1.39-.06-1.84a11.09 11.09 0 0 0-.87-3.74 7.13 7.13 0 0 0-2.33-3 6.74 6.74 0 0 0-4.11-1.16 6.85 6.85 0 0 0-4.17 1.16 6.49 6.49 0 0 0-2.24 3 14.43 14.43 0 0 0-.79 4.14v2.84a14.62 14.62 0 0 0 .79 4.11 6.52 6.52 0 0 0 2.24 3.06 6.85 6.85 0 0 0 4.17 1.23ZM190.13 52.07a1.38 1.38 0 0 1-1-.37 1.31 1.31 0 0 1-.37-.94V23.89a1.31 1.31 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.63a1.25 1.25 0 0 1 1.31 1.3v2.44a12.29 12.29 0 0 1 3.8-3.09 11.91 11.91 0 0 1 5.72-1.24 11.34 11.34 0 0 1 6.12 1.59 10.28 10.28 0 0 1 3.92 4.41 15.55 15.55 0 0 1 1.37 6.77v16a1.25 1.25 0 0 1-.4.94 1.39 1.39 0 0 1-1 .37h-3.92a1.28 1.28 0 0 1-.93-.37 1.27 1.27 0 0 1-.37-.94V35.06a8.33 8.33 0 0 0-1.79-5.67 6.45 6.45 0 0 0-5.13-2 6.84 6.84 0 0 0-5.18 2 7.83 7.83 0 0 0-2 5.67v15.7a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37ZM230.92 52.63a11.76 11.76 0 0 1-5.25-1.16 9.93 9.93 0 0 1-3.74-3.09 7.31 7.31 0 0 1-1.39-4.38 7.42 7.42 0 0 1 3.18-6.27 18.1 18.1 0 0 1 8.39-3.09l8-1.19v-1.4a5.5 5.5 0 0 0-1.36-4c-.91-.94-2.42-1.41-4.54-1.41a6.9 6.9 0 0 0-3.74.9 5.22 5.22 0 0 0-2 2.44 1.32 1.32 0 0 1-1.25.74h-3.51a1.2 1.2 0 0 1-.94-.34 1.34 1.34 0 0 1-.31-.91 5.88 5.88 0 0 1 .71-2.12 8.28 8.28 0 0 1 2.07-2.5 11.76 11.76 0 0 1 3.66-2 15.47 15.47 0 0 1 5.35-.82 17.06 17.06 0 0 1 5.9.88 9.93 9.93 0 0 1 3.8 2.35 9.17 9.17 0 0 1 2.07 3.4 12.46 12.46 0 0 1 .65 4v18.1a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37h-3.63a1.25 1.25 0 0 1-1.3-1.31v-2.38a9.3 9.3 0 0 1-2 2 10.74 10.74 0 0 1-3 1.62 13.85 13.85 0 0 1-4.42.63Zm1.59-4.82a8.24 8.24 0 0 0 3.91-.93A6.74 6.74 0 0 0 239.2 44a10.38 10.38 0 0 0 1-4.88v-1.4l-6.13 1a13.25 13.25 0 0 0-5.52 1.75 3.66 3.66 0 0 0-1.85 3.06 3.46 3.46 0 0 0 .85 2.41 5.33 5.33 0 0 0 2.16 1.42 8 8 0 0 0 2.8.45ZM254.47 52.07a1.25 1.25 0 0 1-1.3-1.31V13.12a1.25 1.25 0 0 1 1.3-1.31h3.74a1.25 1.25 0 0 1 1.3 1.31v37.64a1.25 1.25 0 0 1-1.3 1.31Z"></path></g></svg></div><ul><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/terms" class="no-underline hover:underline">Terms & Conditions</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/privacy-policy" class="no-underline hover:underline">Privacy Policy</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/cookie-policy" class="no-underline hover:underline">Cookie Policy</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://docs.appsignal.com/appsignal/gdpr.html" class="no-underline hover:underline">GDPR compliance</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/contact" class="no-underline hover:underline">Contact us / Imprint</a></li></ul></div></div></div></footer></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"post":{"categoryConfig":{"name":"Elixir","theme":{"style":{"textColor":"text-purple-500","textColorLight":"text-purple-200","bgColor":"bg-purple-800","bgColorVariant":"bg-purple-700"},"menu":"c_h-tabs__tab--active c_h-tabs__tab--purple"}},"authors":[{"about":"Guest author Allan is a software engineer and entrepreneur based in Canada. He has 15 years of industry experience, and is passionate about functional programming and Elixir development.","name":"Allan MacGregor","image":"/images/authors/allan.jpg","slug":"allan-macgregor"}],"series":[{"title":"Using Dependency Injection in Elixir","slug":"2024/05/21/using-dependency-injection-in-elixir","date":"2024-05-21T00:00:00+00:00","active":true},{"title":"Advanced Dependency Injection in Elixir with Rewire","slug":"2024/06/11/advanced-dependency-injection-in-elixir-with-rewire","date":"2024-06-11T00:00:00+00:00","active":false}],"slug":"2024/05/21/using-dependency-injection-in-elixir","socialImg":"https://ondemand.bannerbear.com/signedurl/Mn62mqoVbWvyB5wgQ1/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IlVzaW5nIERlcGVuZGVuY3kgSW5qZWN0aW9uIGluIEVsaXhpciJ9LHsibmFtZSI6ImltYWdlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly9hcHBzaWduYWwtbmV4dGpzLWJsb2ctbDRkNDR4OGx5LWFwcHNpZ25hbC52ZXJjZWwuYXBwL2ltYWdlcy9ibG9nLzIwMjQtMDUvZGVwZW5kZW5jeS1pbmplY3Rpb24tcDEuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLWw0ZDQ0eDhseS1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d\u0026s=c99c1e372af5921cc9113eb727618a422207ff7cf155970814dcbcb9f917057f","jsonLd":{"type":"BlogPosting","url":"https://blog.appsignal.com/2024/05/21/using-dependency-injection-in-elixir.html","title":"Using Dependency Injection in Elixir","description":"Dependency injection can prove useful in Elixir. In this first part of a two-part series, we'll look at some basic concepts, core principles, and types of dependency injection.","images":["https://ondemand.bannerbear.com/signedurl/Mn62mqoVbWvyB5wgQ1/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IlVzaW5nIERlcGVuZGVuY3kgSW5qZWN0aW9uIGluIEVsaXhpciJ9LHsibmFtZSI6ImltYWdlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly9hcHBzaWduYWwtbmV4dGpzLWJsb2ctbDRkNDR4OGx5LWFwcHNpZ25hbC52ZXJjZWwuYXBwL2ltYWdlcy9ibG9nLzIwMjQtMDUvZGVwZW5kZW5jeS1pbmplY3Rpb24tcDEuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLWw0ZDQ0eDhseS1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d\u0026amp;s=c99c1e372af5921cc9113eb727618a422207ff7cf155970814dcbcb9f917057f"],"datePublished":"2024-05-21T00:00:00+00:00","dateModified":"2024-05-21T00:00:00+00:00","authorName":[{"type":"Person","name":"Allan MacGregor","url":"https://blog.appsignal.com/authors/allan-macgregor.html"}],"publisherName":"AppSignal","publisherLogo":"https://appsignal.com/images/logo-appsignal.svg"},"title":"Using Dependency Injection in Elixir","intro":"Dependency injection can prove useful in Elixir. In this first part of a two-part series, we'll look at some basic concepts, core principles, and types of dependency injection.","category":"elixir","tags":"elixir, elixir-alchemy","author":"Allan MacGregor","image":"/images/blog/2024-05/dependency-injection-p1.jpg","serie":"Dependency Injection with ExUnit and Rewire for Elixir","bannerbear":"illustration","date":"2024-05-21T00:00:00+00:00","body":{"raw":"\nWhile controversial in functional programming, dependency injection can be a useful pattern in Elixir for managing dependencies and improving testability.\n\nIn this, the first part of a two-part series, we will cover the basic concepts, core principles, and types of dependency injection. We'll explore its benefits in terms of modularity, testability, and maintainability.\n\nThen, we will look into a specific scenario where dependency injection can be beneficial, in this case, testing.\n\nLet's first explain what dependency injection is.\n\n## What Is Dependency Injection?\n\nDependency Injection (DI) is a software design pattern that involves supplying an external dependency to a component rather than allowing the component to create the dependency itself. This pattern is a form of Inversion of Control (IoC), where control over the dependencies is inverted from the component to an external entity.\n\nThe main goal of DI is to reduce coupling between components, making our system more modular, flexible to changes, and easier to test.\n\nThese are the core concepts of dependency injection:\n\n- **Dependency**: An entity that another entity depends on to function properly.\n- **Injector**: The mechanism that injects dependencies into a component.\n- **Client**: The component that depends on the provided dependencies to operate.\n- **Service**: The dependency that the client component uses.\n\n### Types of Dependency Injection\n\n1. **Constructor Injection**: The dependencies are provided through the component's constructor.\n2. **Setter Injection**: The dependencies are provided through setter methods or properties.\n3. **Interface Injection**: The dependency provides an injector method that will inject the dependency into any client passed to it.\n\n### Advantages of Dependency Injection\n\n- **Reduced Coupling**: By decoupling components from their dependencies, systems become more modular, allowing for easier maintenance and scalability.\n- **Increased Flexibility**: Changing or updating dependencies does not require changes to the dependent components, making the system more adaptable.\n- **Improved Testability**: Dependencies can be easily mocked or stubbed in tests, allowing for more isolated and reliable testing.\n\n### How Dependency Injection Works\n\nThere are four steps you should take to leverage dependency injection in your program or service:\n\n1. **Define the Service Interfaces**: These interfaces represent the abstract contracts that services must fulfill.\n2. **Implement the Services**: Concrete implementations of the service interfaces are developed.\n3. **Configure the Injector**: The injector is configured to know which service implementations to inject into which clients.\n4. **Inject Dependencies**: When a client is instantiated, the injector supplies it with the required service implementations based on the configuration.\n\n### Dependency Injection Diagram\n\nThe following diagram illustrates the basic concept of dependency injection:\n\n\n\n- The **Client** requires a service interface to perform its function.\n- The **Dependency Injector** decides which implementation of the service interface (`Service A` or `Service B`) to inject into the client at runtime.\n- **Service A** and **Service B** are different implementations of the same service interface. The injector injects one of these into the client based on the configuration or conditions.\n\nThis pattern allows for high flexibility and decoupling of components within software applications, facilitating easier management, testing, and evolution of the application code.\n\n## How Can Dependency Injection Be Applied in Elixir?\n\nAs we mentioned earlier, dependency injection is a pattern that is more commonly associated with object-oriented programming languages. Functional programming languages like Elixir offer a different set of tools and idioms for managing dependencies and state. However, the principles of DI can still be applied in Elixir to achieve similar benefits.\n\nIn Elixir, the emphasis on explicit over implicit dependency management aligns well with DI principles. For testing purposes, DI allows developers to easily replace real implementations with mocks or stubs, facilitating isolated unit tests that are not dependent on external services or state. This approach enhances test reliability and execution speed, as tests become less brittle and more focused on the functionality being tested.\n\n\u003cBanner lang=\"elixir\" /\u003e\n\n### Practical Application of Dependency Injection in Elixir for Testing\n\nlet's look at how we can use dependency injection to inject mocks and configure dependencies in Elixir.\n\n#### Injecting Mocks\n\nOne common application of DI in Elixir testing involves injecting mock modules or functions that simulate the behavior of real dependencies. This technique is particularly useful when dealing with external services like databases or APIs.\n\n```elixir\ndefmodule MyApp.MyModule do\n def fetch_data(dataSource) do\n dataSource.query()\n end\nend\n\ndefmodule MyApp.MyModuleTest do\n use ExUnit.Case\n\n test \"fetch_data returns expected result\" do\n mockDataSource = %{\n query: fn -\u003e {:ok, \"mocked data\"} end\n }\n\n assert MyApp.MyModule.fetch_data(mockDataSource) == {:ok, \"mocked data\"}\n end\nend\n```\n\nIn this example, `MyApp.MyModule.fetch_data/1` depends on a `dataSource` that responds to a `query` function. During tests, a mock `dataSource` is injected, allowing the test to run independently of any external data sources.\n\n#### Configurable Dependencies\n\nAnother DI strategy involves using application configuration to define dependencies, which can then be overridden in the test environment.\n\n```elixir\n# config/config.exs\nconfig :my_app, data_service: MyApp.DataService\n\n# config/test.exs\nconfig :my_app, data_service: MyApp.MockDataService\n```\n\nIn your application code, you would fetch the dependency from the application configuration:\n\n```elixir\ndefmodule MyApp.MyModule do\n def fetch_data do\n dataSource = Application.get_env(:my_app, :data_service)\n dataSource.query()\n end\nend\n```\n\nThis simple example shows how DI can be achieved in Elixir by configuring dependencies at runtime, allowing for easy substitution of real implementations with mocks or stubs during testing.\n\nNext, let's review a more practical example that uses DI to inject a mock service into a module for testing.\n\n## Testing with Dependency Injection\n\nIn this example, we will work on `EmailScanner`, a module that scans emails for spam. We will use a `SpamFilterService` to check if emails are spam and dependency injection to inject a mock `SpamFilterService` for testing.\n\nStart by creating a new Elixir project:\n\n```bash\nmix new email_scanner\n```\n\nNow let's move on to implementation and testing.\n\n### Implementation with `ExUnit`\n\nFirst, create the `EmailScanner` module. This module will depend on a `SpamFilterService` to check if emails are spam. In this case, the `SpamFilterService` will be injected as a dependency, making it easy to swap with a mock during testing.\n\n```elixir\ndefmodule EmailScanner do\n def scan_email(spam_filter_service, email) do\n spam_filter_service.check_spam(email)\n end\nend\n```\n\n### Testing `EmailScanner` with `ExUnit`\n\nNow, let's write a test for the `EmailScanner` module using `ExUnit`. We'll create a mock `SpamFilterService` to inject during tests:\n\n```elixir\ndefmodule MockSpamFilterService do\n def check_spam(_email), do: false\nend\n```\n\nIn this mock, the `check_spam/1` function always returns `false`, simulating a non-spam email. Next, let's create a test case that makes use of our new mock:\n\n```elixir\ndefmodule EmailScannerTest do\n use ExUnit.Case\n\n test \"scan_email with non-spam email returns false\" do\n non_spam_email = %Email{content: \"Hello, world!\"}\n assert false == EmailScanner.scan_email(MockSpamFilterService, non_spam_email)\n end\nend\n```\n\nThis test injects `MockSpamFilterService` into `EmailScanner`, isolating the test from the real spam filtering logic and focusing solely on the `EmailScanner`'s behavior.\n\nBy doing this, we can decouple the `EmailScanner` module from the `SpamFilterService`, making it easier to test and maintain.\n\nNow that we've taken a look at using `ExUnit` and testing, let's turn to some common dependency injection mistakes to avoid and best practices.\n\n## Common Dependency Injection Pitfalls and Best Practices\n\nFirst, we'll touch on some pitfalls:\n\n1. **Over-Reliance on Mocks**: While DI makes it easy to replace real implementations with mocks, overusing mocks can lead to fragile tests that are overly focused on implementation details rather than behavior.\n2. **Complex Dependency Graphs**: Introducing DI without careful planning can lead to a tangled web of dependencies that are hard to manage and understand.\n3. **Ignoring the Complexity of Configuration**: DI often requires some form of configuration to wire up dependencies. This configuration can become complex and unwieldy if not managed properly.\n\nTo help you avoid these pitfalls, here are some best practices to follow:\n\n1. **Define Clear Interfaces**: Ensure that your dependencies have clearly defined interfaces.\n2. **Use Configuration Wisely**: Be mindful of the complexity that configuration can introduce.\n3. **Leverage Elixir’s Capabilities**: Take advantage of Elixir’s features, such as module attributes and configuration files, to manage your dependencies effectively.\n4. **Test with Real Implementations When Possible**: While mocking is useful, also test with real implementations to ensure that your system works as expected in real-world scenarios.\n\nThat's it for this part of the series!\n\n## Wrapping Up and What's Next\n\nIn this article, we have covered the basic concepts of dependency injection, its application in Elixir, and how it can be leveraged for testing. We have also discussed common pitfalls to avoid and best practices to follow when implementing DI in Elixir.\n\nIn the next and final part of this series, we'll look specifically at advanced dependency injection in Elixir using Rewire.\n\nUntil then, happy coding!\n\n**P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, [subscribe to our Elixir Alchemy newsletter and never miss a single post](/elixir-alchemy)!**\n","code":"var Component=(()=\u003e{var h=Object.create;var a=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var m=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var u=(i,e)=\u003e()=\u003e(e||i((e={exports:{}}).exports,e),e.exports),f=(i,e)=\u003e{for(var l in e)a(i,l,{get:e[l],enumerable:!0})},c=(i,e,l,r)=\u003e{if(e\u0026\u0026typeof e==\"object\"||typeof e==\"function\")for(let t of y(e))!g.call(i,t)\u0026\u0026t!==l\u0026\u0026a(i,t,{get:()=\u003ee[t],enumerable:!(r=p(e,t))||r.enumerable});return i};var E=(i,e,l)=\u003e(l=i!=null?h(m(i)):{},c(e||!i||!i.__esModule?a(l,\"default\",{value:i,enumerable:!0}):l,i)),F=i=\u003ec(a({},\"__esModule\",{value:!0}),i);var d=u((I,o)=\u003e{o.exports=_jsx_runtime});var k={};f(k,{default:()=\u003ev,frontmatter:()=\u003ew});var n=E(d()),w={title:\"Using Dependency Injection in Elixir\",author:\"Allan MacGregor\",intro:\"Dependency injection can prove useful in Elixir. In this first part of a two-part series, we'll look at some basic concepts, core principles, and types of dependency injection.\",image:\"/images/blog/2024-05/dependency-injection-p1.jpg\",tags:\"elixir, elixir-alchemy\",category:\"elixir\",bannerbear:\"illustration\",serie:\"Dependency Injection with ExUnit and Rewire for Elixir\"};function s(i){let e=Object.assign({p:\"p\",h2:\"h2\",ul:\"ul\",li:\"li\",strong:\"strong\",h3:\"h3\",ol:\"ol\",img:\"img\",code:\"code\",h4:\"h4\",figure:\"figure\",pre:\"pre\",span:\"span\",a:\"a\"},i.components),{Banner:l}=e;return l||b(\"Banner\",!0),(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(e.p,{children:\"While controversial in functional programming, dependency injection can be a useful pattern in Elixir for managing dependencies and improving testability.\"}),`\n`,(0,n.jsx)(e.p,{children:\"In this, the first part of a two-part series, we will cover the basic concepts, core principles, and types of dependency injection. We'll explore its benefits in terms of modularity, testability, and maintainability.\"}),`\n`,(0,n.jsx)(e.p,{children:\"Then, we will look into a specific scenario where dependency injection can be beneficial, in this case, testing.\"}),`\n`,(0,n.jsx)(e.p,{children:\"Let's first explain what dependency injection is.\"}),`\n`,(0,n.jsx)(e.h2,{id:\"what-is-dependency-injection\",children:\"What Is Dependency Injection?\"}),`\n`,(0,n.jsx)(e.p,{children:\"Dependency Injection (DI) is a software design pattern that involves supplying an external dependency to a component rather than allowing the component to create the dependency itself. This pattern is a form of Inversion of Control (IoC), where control over the dependencies is inverted from the component to an external entity.\"}),`\n`,(0,n.jsx)(e.p,{children:\"The main goal of DI is to reduce coupling between components, making our system more modular, flexible to changes, and easier to test.\"}),`\n`,(0,n.jsx)(e.p,{children:\"These are the core concepts of dependency injection:\"}),`\n`,(0,n.jsxs)(e.ul,{children:[`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Dependency\"}),\": An entity that another entity depends on to function properly.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Injector\"}),\": The mechanism that injects dependencies into a component.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Client\"}),\": The component that depends on the provided dependencies to operate.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Service\"}),\": The dependency that the client component uses.\"]}),`\n`]}),`\n`,(0,n.jsx)(e.h3,{id:\"types-of-dependency-injection\",children:\"Types of Dependency Injection\"}),`\n`,(0,n.jsxs)(e.ol,{children:[`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Constructor Injection\"}),\": The dependencies are provided through the component's constructor.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Setter Injection\"}),\": The dependencies are provided through setter methods or properties.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Interface Injection\"}),\": The dependency provides an injector method that will inject the dependency into any client passed to it.\"]}),`\n`]}),`\n`,(0,n.jsx)(e.h3,{id:\"advantages-of-dependency-injection\",children:\"Advantages of Dependency Injection\"}),`\n`,(0,n.jsxs)(e.ul,{children:[`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Reduced Coupling\"}),\": By decoupling components from their dependencies, systems become more modular, allowing for easier maintenance and scalability.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Increased Flexibility\"}),\": Changing or updating dependencies does not require changes to the dependent components, making the system more adaptable.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Improved Testability\"}),\": Dependencies can be easily mocked or stubbed in tests, allowing for more isolated and reliable testing.\"]}),`\n`]}),`\n`,(0,n.jsx)(e.h3,{id:\"how-dependency-injection-works\",children:\"How Dependency Injection Works\"}),`\n`,(0,n.jsx)(e.p,{children:\"There are four steps you should take to leverage dependency injection in your program or service:\"}),`\n`,(0,n.jsxs)(e.ol,{children:[`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Define the Service Interfaces\"}),\": These interfaces represent the abstract contracts that services must fulfill.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Implement the Services\"}),\": Concrete implementations of the service interfaces are developed.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Configure the Injector\"}),\": The injector is configured to know which service implementations to inject into which clients.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Inject Dependencies\"}),\": When a client is instantiated, the injector supplies it with the required service implementations based on the configuration.\"]}),`\n`]}),`\n`,(0,n.jsx)(e.h3,{id:\"dependency-injection-diagram\",children:\"Dependency Injection Diagram\"}),`\n`,(0,n.jsx)(e.p,{children:\"The following diagram illustrates the basic concept of dependency injection:\"}),`\n`,(0,n.jsx)(e.img,{src:\"/images/blog/2024-05/dependency-injection-diagram.png\",alt:\"Dependency Injection Diagram\",width:\"1320\",height:\"890\"}),`\n`,(0,n.jsxs)(e.ul,{children:[`\n`,(0,n.jsxs)(e.li,{children:[\"The \",(0,n.jsx)(e.strong,{children:\"Client\"}),\" requires a service interface to perform its function.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[\"The \",(0,n.jsx)(e.strong,{children:\"Dependency Injector\"}),\" decides which implementation of the service interface (\",(0,n.jsx)(e.code,{children:\"Service A\"}),\" or \",(0,n.jsx)(e.code,{children:\"Service B\"}),\") to inject into the client at runtime.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Service A\"}),\" and \",(0,n.jsx)(e.strong,{children:\"Service B\"}),\" are different implementations of the same service interface. The injector injects one of these into the client based on the configuration or conditions.\"]}),`\n`]}),`\n`,(0,n.jsx)(e.p,{children:\"This pattern allows for high flexibility and decoupling of components within software applications, facilitating easier management, testing, and evolution of the application code.\"}),`\n`,(0,n.jsx)(e.h2,{id:\"how-can-dependency-injection-be-applied-in-elixir\",children:\"How Can Dependency Injection Be Applied in Elixir?\"}),`\n`,(0,n.jsx)(e.p,{children:\"As we mentioned earlier, dependency injection is a pattern that is more commonly associated with object-oriented programming languages. Functional programming languages like Elixir offer a different set of tools and idioms for managing dependencies and state. However, the principles of DI can still be applied in Elixir to achieve similar benefits.\"}),`\n`,(0,n.jsx)(e.p,{children:\"In Elixir, the emphasis on explicit over implicit dependency management aligns well with DI principles. For testing purposes, DI allows developers to easily replace real implementations with mocks or stubs, facilitating isolated unit tests that are not dependent on external services or state. This approach enhances test reliability and execution speed, as tests become less brittle and more focused on the functionality being tested.\"}),`\n`,(0,n.jsx)(l,{lang:\"elixir\"}),`\n`,(0,n.jsx)(e.h3,{id:\"practical-application-of-dependency-injection-in-elixir-for-testing\",children:\"Practical Application of Dependency Injection in Elixir for Testing\"}),`\n`,(0,n.jsx)(e.p,{children:\"let's look at how we can use dependency injection to inject mocks and configure dependencies in Elixir.\"}),`\n`,(0,n.jsx)(e.h4,{id:\"injecting-mocks\",children:\"Injecting Mocks\"}),`\n`,(0,n.jsx)(e.p,{children:\"One common application of DI in Elixir testing involves injecting mock modules or functions that simulate the behavior of real dependencies. This technique is particularly useful when dealing with external services like databases or APIs.\"}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defmodule MyApp.MyModule do\n def fetch_data(dataSource) do\n dataSource.query()\n end\nend\n\ndefmodule MyApp.MyModuleTest do\n use ExUnit.Case\n\n test \"fetch_data returns expected result\" do\n mockDataSource = %{\n query: fn -\u003e {:ok, \"mocked data\"} end\n }\n\n assert MyApp.MyModule.fetch_data(mockDataSource) == {:ok, \"mocked data\"}\n end\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" MyApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"MyModule\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" fetch_data\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(dataSource) \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" data\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Source\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"query\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"()\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" MyApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"MyModuleTest\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" use\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" ExUnit\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Case\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" test \"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:'\"fetch_data returns expected result\"'}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" mockDataSource \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"=\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" %{\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" query:\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" fn\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" -\u003e\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":ok\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:'\"mocked data\"'}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"} \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" }\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" assert \"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"MyApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"MyModule\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"fetch_data\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(mockDataSource) \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"==\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":ok\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:'\"mocked data\"'}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"In this example, \",(0,n.jsx)(e.code,{children:\"MyApp.MyModule.fetch_data/1\"}),\" depends on a \",(0,n.jsx)(e.code,{children:\"dataSource\"}),\" that responds to a \",(0,n.jsx)(e.code,{children:\"query\"}),\" function. During tests, a mock \",(0,n.jsx)(e.code,{children:\"dataSource\"}),\" is injected, allowing the test to run independently of any external data sources.\"]}),`\n`,(0,n.jsx)(e.h4,{id:\"configurable-dependencies\",children:\"Configurable Dependencies\"}),`\n`,(0,n.jsx)(e.p,{children:\"Another DI strategy involves using application configuration to define dependencies, which can then be overridden in the test environment.\"}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`# config/config.exs\nconfig :my_app, data_service: MyApp.DataService\n\n# config/test.exs\nconfig :my_app, data_service: MyApp.MockDataService\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"# config/config.exs\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"config \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":my_app\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"data_service:\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" MyApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"DataService\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"# config/test.exs\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"config \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":my_app\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"data_service:\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" MyApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"MockDataService\"})]})]})})}),`\n`,(0,n.jsx)(e.p,{children:\"In your application code, you would fetch the dependency from the application configuration:\"}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defmodule MyApp.MyModule do\n def fetch_data do\n dataSource = Application.get_env(:my_app, :data_service)\n dataSource.query()\n end\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" MyApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"MyModule\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" fetch_data\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" dataSource \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"=\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Application\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"get_env\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":my_app\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":data_service\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\")\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" data\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Source\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"query\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"()\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsx)(e.p,{children:\"This simple example shows how DI can be achieved in Elixir by configuring dependencies at runtime, allowing for easy substitution of real implementations with mocks or stubs during testing.\"}),`\n`,(0,n.jsx)(e.p,{children:\"Next, let's review a more practical example that uses DI to inject a mock service into a module for testing.\"}),`\n`,(0,n.jsx)(e.h2,{id:\"testing-with-dependency-injection\",children:\"Testing with Dependency Injection\"}),`\n`,(0,n.jsxs)(e.p,{children:[\"In this example, we will work on \",(0,n.jsx)(e.code,{children:\"EmailScanner\"}),\", a module that scans emails for spam. We will use a \",(0,n.jsx)(e.code,{children:\"SpamFilterService\"}),\" to check if emails are spam and dependency injection to inject a mock \",(0,n.jsx)(e.code,{children:\"SpamFilterService\"}),\" for testing.\"]}),`\n`,(0,n.jsx)(e.p,{children:\"Start by creating a new Elixir project:\"}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"bash\",\"data-theme\":\"github-dark\",\"data-raw\":`mix new email_scanner\n`,children:(0,n.jsx)(e.code,{\"data-language\":\"bash\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"mix\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" new\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" email_scanner\"})]})})})}),`\n`,(0,n.jsx)(e.p,{children:\"Now let's move on to implementation and testing.\"}),`\n`,(0,n.jsxs)(e.h3,{id:\"implementation-with-exunit\",children:[\"Implementation with \",(0,n.jsx)(e.code,{children:\"ExUnit\"})]}),`\n`,(0,n.jsxs)(e.p,{children:[\"First, create the \",(0,n.jsx)(e.code,{children:\"EmailScanner\"}),\" module. This module will depend on a \",(0,n.jsx)(e.code,{children:\"SpamFilterService\"}),\" to check if emails are spam. In this case, the \",(0,n.jsx)(e.code,{children:\"SpamFilterService\"}),\" will be injected as a dependency, making it easy to swap with a mock during testing.\"]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defmodule EmailScanner do\n def scan_email(spam_filter_service, email) do\n spam_filter_service.check_spam(email)\n end\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" EmailScanner\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" scan_email\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(spam_filter_service, email) \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" spam_filter_service.\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"check_spam\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(email)\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsxs)(e.h3,{id:\"testing-emailscanner-with-exunit\",children:[\"Testing \",(0,n.jsx)(e.code,{children:\"EmailScanner\"}),\" with \",(0,n.jsx)(e.code,{children:\"ExUnit\"})]}),`\n`,(0,n.jsxs)(e.p,{children:[\"Now, let's write a test for the \",(0,n.jsx)(e.code,{children:\"EmailScanner\"}),\" module using \",(0,n.jsx)(e.code,{children:\"ExUnit\"}),\". We'll create a mock \",(0,n.jsx)(e.code,{children:\"SpamFilterService\"}),\" to inject during tests:\"]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defmodule MockSpamFilterService do\n def check_spam(_email), do: false\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" MockSpamFilterService\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" check_spam\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(\"}),(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"_email\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"), \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"do:\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" false\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"In this mock, the \",(0,n.jsx)(e.code,{children:\"check_spam/1\"}),\" function always returns \",(0,n.jsx)(e.code,{children:\"false\"}),\", simulating a non-spam email. Next, let's create a test case that makes use of our new mock:\"]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defmodule EmailScannerTest do\n use ExUnit.Case\n\n test \"scan_email with non-spam email returns false\" do\n non_spam_email = %Email{content: \"Hello, world!\"}\n assert false == EmailScanner.scan_email(MockSpamFilterService, non_spam_email)\n end\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" EmailScannerTest\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" use\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" ExUnit\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Case\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" test \"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:'\"scan_email with non-spam email returns false\"'}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" non_spam_email \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"=\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" %\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Email\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"{\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"content:\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:' \"Hello, world!\"'}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" assert \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"false\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" ==\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" EmailScanner\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"scan_email\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"MockSpamFilterService\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", non_spam_email)\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"This test injects \",(0,n.jsx)(e.code,{children:\"MockSpamFilterService\"}),\" into \",(0,n.jsx)(e.code,{children:\"EmailScanner\"}),\", isolating the test from the real spam filtering logic and focusing solely on the \",(0,n.jsx)(e.code,{children:\"EmailScanner\"}),\"'s behavior.\"]}),`\n`,(0,n.jsxs)(e.p,{children:[\"By doing this, we can decouple the \",(0,n.jsx)(e.code,{children:\"EmailScanner\"}),\" module from the \",(0,n.jsx)(e.code,{children:\"SpamFilterService\"}),\", making it easier to test and maintain.\"]}),`\n`,(0,n.jsxs)(e.p,{children:[\"Now that we've taken a look at using \",(0,n.jsx)(e.code,{children:\"ExUnit\"}),\" and testing, let's turn to some common dependency injection mistakes to avoid and best practices.\"]}),`\n`,(0,n.jsx)(e.h2,{id:\"common-dependency-injection-pitfalls-and-best-practices\",children:\"Common Dependency Injection Pitfalls and Best Practices\"}),`\n`,(0,n.jsx)(e.p,{children:\"First, we'll touch on some pitfalls:\"}),`\n`,(0,n.jsxs)(e.ol,{children:[`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Over-Reliance on Mocks\"}),\": While DI makes it easy to replace real implementations with mocks, overusing mocks can lead to fragile tests that are overly focused on implementation details rather than behavior.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Complex Dependency Graphs\"}),\": Introducing DI without careful planning can lead to a tangled web of dependencies that are hard to manage and understand.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Ignoring the Complexity of Configuration\"}),\": DI often requires some form of configuration to wire up dependencies. This configuration can become complex and unwieldy if not managed properly.\"]}),`\n`]}),`\n`,(0,n.jsx)(e.p,{children:\"To help you avoid these pitfalls, here are some best practices to follow:\"}),`\n`,(0,n.jsxs)(e.ol,{children:[`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Define Clear Interfaces\"}),\": Ensure that your dependencies have clearly defined interfaces.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Use Configuration Wisely\"}),\": Be mindful of the complexity that configuration can introduce.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Leverage Elixir\\u2019s Capabilities\"}),\": Take advantage of Elixir\\u2019s features, such as module attributes and configuration files, to manage your dependencies effectively.\"]}),`\n`,(0,n.jsxs)(e.li,{children:[(0,n.jsx)(e.strong,{children:\"Test with Real Implementations When Possible\"}),\": While mocking is useful, also test with real implementations to ensure that your system works as expected in real-world scenarios.\"]}),`\n`]}),`\n`,(0,n.jsx)(e.p,{children:\"That's it for this part of the series!\"}),`\n`,(0,n.jsx)(e.h2,{id:\"wrapping-up-and-whats-next\",children:\"Wrapping Up and What's Next\"}),`\n`,(0,n.jsx)(e.p,{children:\"In this article, we have covered the basic concepts of dependency injection, its application in Elixir, and how it can be leveraged for testing. We have also discussed common pitfalls to avoid and best practices to follow when implementing DI in Elixir.\"}),`\n`,(0,n.jsx)(e.p,{children:\"In the next and final part of this series, we'll look specifically at advanced dependency injection in Elixir using Rewire.\"}),`\n`,(0,n.jsx)(e.p,{children:\"Until then, happy coding!\"}),`\n`,(0,n.jsx)(e.p,{children:(0,n.jsxs)(e.strong,{children:[\"P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, \",(0,n.jsx)(e.a,{href:\"/elixir-alchemy\",children:\"subscribe to our Elixir Alchemy newsletter and never miss a single post\"}),\"!\"]})})]})}function x(i={}){let{wrapper:e}=i.components||{};return e?(0,n.jsx)(e,Object.assign({},i,{children:(0,n.jsx)(s,i)})):s(i)}var v=x;function b(i,e){throw new Error(\"Expected \"+(e?\"component\":\"object\")+\" `\"+i+\"` to be defined: you likely forgot to import, pass, or provide it.\")}return F(k);})();\n;return Component;"},"_id":"posts/2024-05-21-using-dependency-injection-in-elixir.html.mdx","_raw":{"sourceFilePath":"posts/2024-05-21-using-dependency-injection-in-elixir.html.mdx","sourceFileName":"2024-05-21-using-dependency-injection-in-elixir.html.mdx","sourceFileDir":"posts","contentType":"mdx","flattenedPath":"posts/2024-05-21-using-dependency-injection-in-elixir.html"},"type":"Post","year":"2024","serieSlug":"dependency-injection-with-exunit-and-rewire-for-elixir","sitemapSlug":"2024/05/21/using-dependency-injection-in-elixir.html"},"posts":{"favorite":[],"related":[{"date":"2025-03-04T00:00:00+00:00","slug":"2025/03/04/an-introduction-to-absinthe-for-elixir-monitoring-with-appsignal","title":"An Introduction to Absinthe for Elixir Monitoring with AppSignal"},{"date":"2025-02-04T00:00:00+00:00","slug":"2025/02/04/building-a-distributed-rate-limiter-in-elixir-with-hashring","title":"Building a Distributed Rate Limiter in Elixir with HashRing"},{"date":"2025-01-21T00:00:00+00:00","slug":"2025/01/21/tracking-errors-in-tesla-with-appsignal-for-elixir","title":"Tracking Errors in Tesla with AppSignal for Elixir"},{"date":"2024-12-17T00:00:00+00:00","slug":"2024/12/17/appsignals-top-5-elixir-posts-in-2024","title":"AppSignal’s Top 5 Elixir Posts in 2024"},{"date":"2024-12-10T00:00:00+00:00","slug":"2024/12/10/distributed-phoenix-deployment-and-scaling","title":"Distributed Phoenix: Deployment and Scaling"},{"date":"2024-11-12T00:00:00+00:00","slug":"2024/11/12/how-to-track-errors-in-oban-for-elixir-using-appsignal","title":"How to Track Errors in Oban for Elixir Using AppSignal"},{"date":"2024-10-29T00:00:00+00:00","slug":"2024/10/29/managing-distributed-state-with-genservers-in-phoenix-and-elixir","title":"Managing Distributed State with GenServers in Phoenix and Elixir"},{"date":"2024-10-08T00:00:00+00:00","slug":"2024/10/08/find-and-fix-n-plus-1-queries-using-appsignal-for-a-phoenix-app-in-elixir","title":"Find and Fix N+1 Queries Using AppSignal for a Phoenix App in Elixir"},{"date":"2024-09-17T00:00:00+00:00","slug":"2024/09/17/a-complete-guide-to-phoenix-for-elixir-monitoring-with-appsignal","title":"A Complete Guide to Phoenix for Elixir Monitoring with AppSignal"},{"date":"2024-09-03T00:00:00+00:00","slug":"2024/09/03/scaling-your-phoenix-app-in-elixir-with-flame","title":"Scaling Your Phoenix App in Elixir with FLAME"}]}},"__N_SSG":true},"page":"/[year]/[month]/[day]/[text]","query":{"year":"2024","month":"05","day":"21","text":"using-dependency-injection-in-elixir"},"buildId":"Slyvv84b2YSnVGyGXaxYw","isFallback":false,"dynamicIds":[2805,3367,1253,1863],"gsp":true,"scriptLoader":[]}</script></body></html>