<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Trey Griffith</title>
    <description>The latest articles on Forem by Trey Griffith (@treygriffith).</description>
    <link>https://forem.com/treygriffith</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F555735%2F9c58579c-4ab1-4302-aa0d-0c3cb3c98a60.jpeg</url>
      <title>Forem: Trey Griffith</title>
      <link>https://forem.com/treygriffith</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/treygriffith"/>
    <language>en</language>
    <item>
      <title>What domain do I use when setting up OAuth for Zendesk?</title>
      <dc:creator>Trey Griffith</dc:creator>
      <pubDate>Thu, 28 Jan 2021 19:53:08 +0000</pubDate>
      <link>https://forem.com/xkit/what-domain-do-i-use-when-setting-up-oauth-for-zendesk-5h13</link>
      <guid>https://forem.com/xkit/what-domain-do-i-use-when-setting-up-oauth-for-zendesk-5h13</guid>
      <description>&lt;p&gt;If you're building a Zendesk integration that allows your users to connect their Zendesk accounts to your app, you have probably noticed that all the &lt;a href="https://support.zendesk.com//hc/en-us/articles/203663836"&gt;Zendesk documentation for OAuth&lt;/a&gt; use the domain &lt;code&gt;{subdomain}.zendesk.com&lt;/code&gt;, and you might be asking: whose subdomain is this? Is it the app developer's or the user's?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The subdomain you use when developing OAuth apps for Zendesk is the &lt;em&gt;user&lt;/em&gt;'s subdomain&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is that it's the &lt;em&gt;user&lt;/em&gt;'s subdomain, not you, the developer's subdomain. If you're building the integration only for internal use, that's the same subdomain so it won't matter, but if you intend for other Zendesk users to make use of it (what Zendesk calls a "global OAuth client"), you'll need to use their subdomain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do I need the user's subdomain?
&lt;/h3&gt;

&lt;p&gt;Why use the customer's subdomain? &lt;a href="https://developer.zendesk.com/apps/docs/publish/global_oauth_intro"&gt;Zendesk's docs&lt;/a&gt; sum it up:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Zendesk maintains separate logins for each Zendesk account or subdomain. When a customer signs in, they're signing into a specific Zendesk subdomain which carries over to OAuth. When a Zendesk account owner uses OAuth to authorize your service, they're authorizing the service for their subdomain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documentation can be a little bit confusing at times, using "your" subdomain at times when they mean "the user's" subdomain. Just know whereever you see &lt;code&gt;{subdomain}&lt;/code&gt;, they're referring to your user, not you.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to get the user's subdomain
&lt;/h3&gt;

&lt;p&gt;Now you might be wondering if Zendesk provides a sort of global subdomain that you can use where Zendesk either determines through cookies, or collects directly from the user, their Zendesk subdomain. The unfortunate answer here is no: you're left to your own devices to determine the user's subdomain &lt;em&gt;before&lt;/em&gt; sending them through the OAuth flow. You'll also need to hang on to this subdomain: it's used not only for the initial authorization request, but also the token request and API calls afterward.&lt;/p&gt;

&lt;p&gt;You'll probably want to model your own subdomain collection after Zendesk's login process pictured here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TBIRgoPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gi6c1l8ogvobah1v29o0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TBIRgoPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gi6c1l8ogvobah1v29o0.png" alt="Zendesk's default subdomain collection screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So if you're building a Zendesk integration, don't forget to build in subdomain collection to make sure you're making requests to the right domain. &lt;/p&gt;

&lt;p&gt;Of course, if you don't want to bother building an entire subdomain collection flow, you can use &lt;a href="https://app.xkit.co/connectors/new/zendesk"&gt;Xkit's Zendesk Connector&lt;/a&gt;. It has built-in subdomain collection through a popup window and it will surface the subdomain to you with every access token it provides.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EYYiCxfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zxaenyecq2kyi1okyfxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EYYiCxfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zxaenyecq2kyi1okyfxm.png" alt="Xkit's Zendesk subdomain collection popup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With one API call you'll get everything you'll need to get access to your user's Zendesk account. And you can &lt;a href="https://xkit.co/pricing"&gt;get started in 30 minutes, for free&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>oauth</category>
      <category>api</category>
      <category>zendesk</category>
    </item>
    <item>
      <title>Why does my GitHub OAuth2 Token not have the scopes I requested?</title>
      <dc:creator>Trey Griffith</dc:creator>
      <pubDate>Wed, 27 Jan 2021 18:18:06 +0000</pubDate>
      <link>https://forem.com/xkit/why-does-my-github-oauth2-token-not-have-the-scopes-i-requested-3nfm</link>
      <guid>https://forem.com/xkit/why-does-my-github-oauth2-token-not-have-the-scopes-i-requested-3nfm</guid>
      <description>&lt;h3&gt;
  
  
  Why doesn't my access token work?
&lt;/h3&gt;

&lt;p&gt;If you're building a GitHub integration into your app that uses GitHub's OAuth authorization method (as opposed to their App authorization, which is similar but has important differences), you may have noticed that sometimes the scopes that your access token has been granted are different from what you requested. This can be a particularly difficult issue to debug since it usually rears its head when you're trying to use an API protected by one of the scopes you requested, but the API request fails for just one of your users. However, when you try to reproduce this issue, it seems to work just fine. What's going on here?&lt;/p&gt;

&lt;p&gt;GitHub makes use of a little known part of the &lt;a href="https://tools.ietf.org/html/rfc6749#section-3.3"&gt;OAuth2 spec&lt;/a&gt; that allows the authorization server (in this case GitHub) to: "fully or partially ignore the scope requested by the client, based on the authorization server policy or the resource owner's instructions". The way GitHub does this is by allowing your user to pick and choose which of your requested scopes to actually grant to your application through a process we call "line item grants".&lt;/p&gt;

&lt;p&gt;As you can see, this is a completely valid use of the OAuth2 spec, but since most providers don't use this, you may not have been ready for it. But not to worry, while the spec includes the ability for the authorization server (GitHub) to ignore your scope, it does provide you some recourse: "if the issued access token scope is different from the one requested by the client, the authorization server MUST include the &lt;code&gt;scope&lt;/code&gt; response parameter to inform the client of the actual scope granted."&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying the Granted Scope
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;scope&lt;/code&gt; parameter in the Access Token response (which you were probably ignoring until now) will contain the scope that your user actually granted you, and which you therefore actually have access to with your token. See &lt;a href="https://docs.github.com/en/free-pro-team@latest/rest/guides/basics-of-authentication#checking-granted-scopes"&gt;GitHub's docs about this process here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, here's where GitHub goes off the rails. While GitHub allows you to specify the &lt;code&gt;scope&lt;/code&gt; you want in your request by &lt;em&gt;space&lt;/em&gt;-delimiting each scope string (e.g. &lt;code&gt;read:org read:user&lt;/code&gt;), which matches the &lt;a href="https://tools.ietf.org/html/rfc6749#section-3.3"&gt;OAuth2 Spec&lt;/a&gt;, it returns &lt;code&gt;scope&lt;/code&gt; by &lt;em&gt;comma&lt;/em&gt;-delimiting the scope strings (e.g. &lt;code&gt;read:org,read:user&lt;/code&gt;) in violation of the specification. Which means you may have to serialize and parse your &lt;code&gt;scope&lt;/code&gt; using different patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scope Normalization
&lt;/h3&gt;

&lt;p&gt;In addition, GitHub transforms the scopes through a process called "&lt;a href="https://docs.github.com/en/free-pro-team@latest/developers/apps/scopes-for-oauth-apps#normalized-scopes"&gt;scope normalization&lt;/a&gt;". Basically, they take your requested scopes and narrow them down to the smallest set of logical scopes that will be granted. So, for example, if you request the &lt;code&gt;repo&lt;/code&gt; scope, but you also request &lt;code&gt;public_repo&lt;/code&gt; and &lt;code&gt;delete:repo&lt;/code&gt;, GitHub will return just &lt;code&gt;repo&lt;/code&gt;, since the other requested scopes are already included in the &lt;code&gt;repo&lt;/code&gt; scope.&lt;/p&gt;

&lt;p&gt;The actual normalization process (which scopes are removed when requesting two or more) is only partially documented, but based on our reading of the documentation and subsequent testing, here's the normalization tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;repo
┣ delete:repo
┣ repo:status
┣ repo:invite
┣ repo_deployment
┣ public_repo
┃ ┗ admin:repo_hook
┃   ┗ write:repo_hook
┃     ┗ read:repo_hook
┗ security_events
admin:org
┗ write:org
  ┗ read:org
admin:public_key
┗ write:public_key
  ┗ read:public_key
user
┣ read:user
┣ user:email
┗ user:follow
write:discussion
┗ read:discussion
write:packages
┗ read:packages
admin:gpg_key
┗ write:gpg_key
  ┗ read:gpgp_key
workflow
gist
notifications
admin:org_hook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;If you're building a GitHub integration, be sure to check the normalized, comma-delimited scopes that your user actually granted you via GitHub's API to make sure that your token has the scopes you expect it to.&lt;/p&gt;

&lt;p&gt;Of course, if you want to avoid building (or heck, even learning) all that, you can use &lt;a href="https://app.xkit.co/connectors/new/github-oauth"&gt;Xkit's GitHub OAuth Connector&lt;/a&gt; and be up and running with always-valid access tokens in a half hour. We have built-in checks to make sure that your tokens have the scopes you expect them to so you don't have to worry about it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>oauth</category>
      <category>github</category>
    </item>
    <item>
      <title>When do Salesforce access tokens expire?</title>
      <dc:creator>Trey Griffith</dc:creator>
      <pubDate>Thu, 21 Jan 2021 23:09:20 +0000</pubDate>
      <link>https://forem.com/xkit/when-do-salesforce-access-tokens-expire-1ih9</link>
      <guid>https://forem.com/xkit/when-do-salesforce-access-tokens-expire-1ih9</guid>
      <description>&lt;p&gt;If you're building a Salesforce integration into your app, particularly a "Connected App" style of integration, and your integration uses &lt;a href="https://help.salesforce.com/articleView?id=remoteaccess_authenticate.htm&amp;amp;type=5"&gt;OAuth to get access to Salesforce's REST APIs&lt;/a&gt;, you may be wondering when the access tokens issued by Salesforce expire.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://tools.ietf.org/html/rfc6749#section-4.2.2"&gt;OAuth 2.0 spec&lt;/a&gt; the &lt;code&gt;expires_in&lt;/code&gt; parameter is included with the Access Token response and provides the lifetime of the returned token in seconds. And while this parameter is extremely common in OAuth implementations, it is merely &lt;em&gt;recommended&lt;/em&gt; and not &lt;em&gt;required&lt;/em&gt;. The Salesforce OAuth implementation does &lt;em&gt;not&lt;/em&gt; use this parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Typical Token Expiration
&lt;/h3&gt;

&lt;p&gt;In our experience at &lt;a href="https://xkit.co"&gt;Xkit&lt;/a&gt;, Salesforce Access Tokens typically expire in 2 hours (7,200 seconds), but this value is not guaranteed to be static—Salesforce could change it at any time with no warning.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Salesforce Access Tokens typically expire in 2 hours&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How to determine token expiration
&lt;/h3&gt;

&lt;p&gt;So what do you do? You have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use your access token until you receive a &lt;code&gt;401&lt;/code&gt; HTTP status code, and only refresh it then&lt;/li&gt;
&lt;li&gt;Use Salesforce's token introspection endpoint to determine when the token expires&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Token Introspection
&lt;/h3&gt;

&lt;p&gt;That's right! While Salesforce does not include an &lt;code&gt;expires_in&lt;/code&gt; parameter, they do have a special token introspection endpoint as part of the &lt;a href="https://tools.ietf.org/html/rfc7662"&gt;extension to the OAuth 2.0 spec&lt;/a&gt;. This endpoint (&lt;a href="https://help.salesforce.com/articleView?id=remoteaccess_oidc_token_introspection_endpoint.htm&amp;amp;type=5"&gt;Salesforce docs here&lt;/a&gt;) returns a JSON object that includes an &lt;code&gt;exp&lt;/code&gt; property. This &lt;code&gt;exp&lt;/code&gt; corresponds to the &lt;a href="https://tools.ietf.org/html/rfc7519#section-4.1.4"&gt;&lt;code&gt;exp&lt;/code&gt; claim of the JWT spec&lt;/a&gt;. Unlike the &lt;code&gt;expires_in&lt;/code&gt; parameter, &lt;code&gt;exp&lt;/code&gt; is a Unix epoch timestamp.&lt;/p&gt;

&lt;p&gt;Here's an example request from the Salesforce docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /services/oauth2/introspect HTTP/1.1
Host: https://mycompany.my.salesforce.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic M01WRzlsS2NQb05JTlZCSVBKamR3MUo5TExNODJIbkZWVlgxOUtZMQp1QTVtdTBRc
UVXaHFLcG9XM3N2RzNYSHJYRGlDUWpLMW1kZ0F2aENzY0E5R0U6MTk1NTI3OTkyNTY3NTI0MTU3MQ==

token=00DR00000009GVP!ARQAQE5XuPV7J4GoOu3wvLZjZI_TxoBpeZpRb6d8AVdII6cz
_BY_uu1PKxGeAjkSvO0LpWoL_qfbQWKlXoz1f2ICNiy.6Ndr&amp;amp;
token_type_hint=access_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And an example response from our own experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: application/json

{"active":true,"scope":"api refresh_token openid","client_id":"3MVG9lKcPoNINVBIPJjdw1J9LLM82HnFVVX19KY1uA5mu0QqEWhqKpoW3svG3XHrXDiCQjK1mdgAvhCscA9GE","username":"user@example.com\",\"sub\":\"https://login.salesforce.com/id/000000000000000000/000000000000000000\",\"token_type\":\"access_token\",\"exp\":1610509606,\"iat\":1610502406,\"nbf\":1610502406}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So if you need to know when your Salesforce Access Token expires, call the introspection endpoint and you can figure it out for yourself. And don't forget to add the special &lt;code&gt;refresh_token&lt;/code&gt; scope so you can refresh your access when it does expire.&lt;/p&gt;

&lt;p&gt;Of course, if you want to avoid building (or heck, even learning) all that, you can use &lt;a href="https://app.xkit.co/connectors/new/salesforce"&gt;Xkit's Salesforce Connector&lt;/a&gt; and be up and running with always-fresh access tokens in a half hour.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>oauth</category>
      <category>salesforce</category>
    </item>
    <item>
      <title>How I learned a language and launched a product in 60 days</title>
      <dc:creator>Trey Griffith</dc:creator>
      <pubDate>Tue, 12 Jan 2021 00:25:23 +0000</pubDate>
      <link>https://forem.com/treygriffith/how-i-learned-a-language-and-launched-a-product-in-60-days-5ekg</link>
      <guid>https://forem.com/treygriffith/how-i-learned-a-language-and-launched-a-product-in-60-days-5ekg</guid>
      <description>&lt;h3&gt;
  
  
  Background: Why I had to pivot
&lt;/h3&gt;

&lt;p&gt;Almost 3 years ago I incorporated Kinesis Inc, the corporate entity behind &lt;a href="https://xkit.co"&gt;Xkit&lt;/a&gt;. But at the time I was building a non-custodial cryptocurrency exchange. After going through Y Combinator and raising $3.5M from some great investors and building a product I was really proud of, I made the hard decision to &lt;a href="https://sparkswap.com"&gt;shut it down&lt;/a&gt; nearly a year ago. We hadn't found an audience that would provide venture scale growth, so it was back to the drawing board.&lt;/p&gt;

&lt;p&gt;I started working on an idea in FinTech, trying to help a friend's company solve a problem with SMB customers not paying on time (or at all) for their services. As I talked to prospective customers about the problem, one thing became abundantly clear: in the time that I had spent away from the SaaS world toiling in the Bitcoin mines, expectations around how SaaS tools work together had radically shifted. It was no longer a "bonus" that you worked with some of their other tools, it was an outright necessity. For the product I was exploring I would be building integrations with Quickbooks, Xero, Stripe, and a few others.&lt;/p&gt;

&lt;p&gt;I asked around if anyone had any good tools to accelerate this process, but the responses I got back were pretty disappointing. Nearly every tool was focused on &lt;em&gt;consumers&lt;/em&gt; of SaaS software plugging together the services that they used. Not nearly as much was out there for &lt;em&gt;producers&lt;/em&gt; of SaaS software trying to build native integrations to the other tools their customers use. Not only that, but the ones that did exist tried to pretend that we live in a world where powerful integrations are easy to design and maintain with a GUI. While I'm a fan of no-code and low-code solutions, the fact remains that for some tasks, leaning on a software development lifecycle is still the most effective and efficient way to build it.&lt;/p&gt;

&lt;p&gt;In the meantime, the US blew up with COVID, throwing the financial markets (which my new FinTech project was heavily dependent on) into turmoil. So like any good (read: desperate) startup founder, I scrapped the FinTech project and started building the tool that I wish had existed to help me build integrations to my prospective customers' other SaaS apps. And despite never having touched the language, I decided to build it in Elixir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing Elixir
&lt;/h3&gt;

&lt;p&gt;I've spent most of my career as a software engineer writing in Javascript and Ruby. I've long been intrigued by the concepts behind functional programming (years ago I wrote a small toy to try to achieve immutability on the web, like a centralized IPFS), and a friend introduced me to Elixir, which incorporated some Ruby syntax, making it a bit friendlier to get started with.&lt;/p&gt;

&lt;p&gt;The concepts behind Erlang/OTP/Elixir make it a great fit for a tool like the one I was building. I knew it would be heavily dependent on outside services, meaning it had to be robust to crashes and other unforeseen events. And I knew high concurrency and low latency would be important features as a piece of developer infrastructure. I also wanted to test out &lt;a href="http://www.paulgraham.com/pypar.html"&gt;Paul Graham's theory of hiring programmers&lt;/a&gt;: choose a good language and great programmers will always want to work for you. And finally, I'd be lying if I said there wasn't a part of me that just wanted to try something new. I had spent the past two years working an idea I had to shut down, my "big pivot" was a non-starter, I had laid off nearly the entire team, and for product development it was just me in my bedroom. So I liked the idea of learning something new while working on my second pivot in 3 months.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning Elixir
&lt;/h3&gt;

&lt;p&gt;I read through &lt;a href="https://elixir-lang.org/getting-started/introduction.html"&gt;Elixir's fabulous language guide&lt;/a&gt;. I mean I really read it, cover to cover, and did pretty much all the examples as they came up. Then I read through a handful of pages in the Phoenix (the most popular web framework for Elixir) guides before I said "f*** it" and started building what became Xkit. My first commit was on May 14, 2020. Commit message: "Initial install with platforms".&lt;/p&gt;

&lt;p&gt;The first week was rough. I wasn't used to moving this slowly, but not only was a learning a new language with features pretty unfamiliar to me like pattern-matching, I was learning a new paradigm. Javascript is whatever you want it to be, which means I had some exposure to FP incidentally, but almost everything I've spent significant time on has had a pretty object-oriented flavor to it, even dating back to when I first got started with PHP. Not to mention the entire ethos of OTP to "fail fast and noisily" which ran counter to so many experiences which focused on avoiding failure at all costs (yes, I've seen some pretty large &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; blocks in my day). I got pretty discouraged at the pace I was moving, and somewhere about 10 days in I was really regretting not just using Node/Express, a stack I was so familiar with it felt like I could write the app in my sleep.&lt;/p&gt;

&lt;p&gt;But I kept at it (probably more out of pride than anything else) and a few days later I felt things start to shift. I still wasn't moving fast, but I could tell I was improving. Things that only a few days ago had felt foreign and impossible to grasp started to make sense. From that point on I started accelerating, and on June 1st, I sent one of my investors &lt;a href="https://www.youtube.com/watch?v=7dJi3sAsHM0"&gt;this video&lt;/a&gt; showing the first version of what became Xkit.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Shortcomings of Elixir and Phoenix
&lt;/h3&gt;

&lt;p&gt;I started off trying to use a pure Phoenix stack including their new real-time front-end, &lt;a href="https://github.com/phoenixframework/phoenix_live_view"&gt;LiveView&lt;/a&gt;. I'll be honest, although I really like Elixir and Phoenix, using Phoenix for the front-end brought me back a bit to my Rails days, and not in a good way. Building a modern web application with those tools felt like I'd be working against the tide. I also am pretty partial to &lt;a href="https://evergreen.segment.com"&gt;Evergreen&lt;/a&gt;, the React UI kit by Segment, and I was bearing down on the 6 week timeline I'd set for myself to launch this product. So I ripped out the Phoenix front-end and converted it to be a back-end only application and built a new front-end in React and Evergreen. I did end up using Phoenix's websocket implementation which was a big help integrating the two, although some pieces are not very well documented.&lt;/p&gt;

&lt;p&gt;When I finally getting close to launching, I ran up against the greatest weakness of my choice to use Elixir: deployment. While the world has moved towards a serverless paradigm, Elixir/Erlang/OTP thrive by handling a lot of the same things that "serverless" promises. That means that to really take advantage of all it has to offer, you're better off deploying it on as close to bare metal as you can. That, in addition to some of the architecture choices I made with Xkit (specifically to provision a custom subdomain for every user), made deployment quite a challenge. Failing to find any good documentation about how to deploy a Phoenix application on AWS, I ended up &lt;a href="https://xkit.co/post/deploying-phoenix-for-elixir-on-aws-ec2"&gt;writing a blog post about our process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You might think it's overkill for a one-person development team to have a one-command deploy process, but I assure you, based on my past experience, it's well worth the effort. You don't want critical DevOps knowledge in one person's head, or worse, on some inscrutable AWS GUI. I want it all in code. Even today, you can take the entire Xkit stack, move it to a new AWS account and have it up and running with a single command in around 30 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Launching Xkit
&lt;/h3&gt;

&lt;p&gt;The delays I hit caused me to miss my six week launch goal by two weeks: I launched to some fellow YC founders on July 12 with ~15 connectors and &lt;a href="https://www.youtube.com/watch?v=7MqwQ3Czejs"&gt;this video&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Two weeks after that I launched on &lt;a href="https://www.producthunt.com/posts/xkit"&gt;Product Hunt&lt;/a&gt; and shortly after that on &lt;a href="https://news.ycombinator.com/item?id=24121290"&gt;Hacker News&lt;/a&gt; with over 25 connectors. Since then we've worked with some really great early customers who have helped us refine the product, and I've been able to hire a few friends to help grow the business.&lt;/p&gt;

&lt;p&gt;We're well over 50 connectors at this point (almost entirely driven by customer requests) and we add so many that we no longer track it as a metric. And while I'm still contributing code, I can safely say that the majority of the coding that I will do for Xkit is now likely behind me. But the process of building Xkit (and learning Elixir in the process) has been incredibly rewarding, and hopefully we're just getting started.&lt;/p&gt;

&lt;p&gt;PS - In case you're wondering about the answer to PG's theory about language being a good hiring tool - my early results indicate that it is. From a single post on &lt;a href="https://elixirforum.com"&gt;Elixir Forum&lt;/a&gt; I met one of the best engineers I've seen, and a few weeks later he recruited one of the best engineers he'd worked with to join the team.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>startup</category>
      <category>showdev</category>
      <category>react</category>
    </item>
  </channel>
</rss>
