<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ivan Wong</title>
    <description>The latest articles on DEV Community by Ivan Wong (@wivarn).</description>
    <link>https://dev.to/wivarn</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%2F661116%2F9d180093-853d-48f1-a81b-26a519e4bd06.png</url>
      <title>DEV Community: Ivan Wong</title>
      <link>https://dev.to/wivarn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wivarn"/>
    <language>en</language>
    <item>
      <title>Servers? What Are Those?</title>
      <dc:creator>Ivan Wong</dc:creator>
      <pubDate>Tue, 20 Jul 2021 04:04:58 +0000</pubDate>
      <link>https://dev.to/wivarn/servers-what-are-those-m6k</link>
      <guid>https://dev.to/wivarn/servers-what-are-those-m6k</guid>
      <description>&lt;p&gt;Last week, I wrote about how the first six weeks at &lt;a href="https://skwirl.io/" rel="noopener noreferrer"&gt;skwirl&lt;/a&gt; went. If you haven't read that blog yet you can &lt;a href="https://skwirl.io/blog/six-weeks" rel="noopener noreferrer"&gt;read it here&lt;/a&gt; and if this is the first time you've heard of skwirl you can &lt;a href="https://skwirl.io/blog/intoducing-skwirl" rel="noopener noreferrer"&gt;learn more here.&lt;/a&gt; This week, I'll give an overview of the backend.&lt;/p&gt;

&lt;p&gt;Kevin pitched skwirl to me about 4 months ago. Before he even finished his pitch I already had some ideas in mind for the tech stack. I wanted to stick with what I already knew to reduce time to market. I also wanted to use bleeding edge tech that I was fairly confident is going to become mainstream in the future. Lastly, I wanted to pick technology that was popular and actively being developed/maintained. My thought process at that point: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We'd start with a web app but we knew that we'd want a mobile app very soon&lt;/li&gt;
&lt;li&gt;The front/backends should be separated&lt;/li&gt;
&lt;li&gt;The backend would serve both the web frontend and a future mobile app&lt;/li&gt;
&lt;li&gt;I spent several years developing Ruby on Rails apps before switching over to DevOps and working on AWS&lt;/li&gt;
&lt;li&gt;Ideally, the backend would be some form of Ruby/ActiveRecord JSON API&lt;/li&gt;
&lt;li&gt;I wanted to run everything on serverless (read to the end to learn why)

&lt;ul&gt;
&lt;li&gt;This narrowed compute choices down to Lambda or Fargate&lt;/li&gt;
&lt;li&gt;Database options would be DynamoDB or Aurora&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;React was a strong contender for the front end&lt;/li&gt;

&lt;li&gt;I have absolutely no front end experience&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So I searched around the internet with the terms &lt;code&gt;ruby lambda&lt;/code&gt;, &lt;code&gt;ruby serverless&lt;/code&gt;, &lt;code&gt;rails lambda&lt;/code&gt;, &lt;code&gt;rails serverless&lt;/code&gt;. Eventually I came across and settled on using &lt;a href="https://rubyonjets.com/" rel="noopener noreferrer"&gt;Ruby on Jets&lt;/a&gt;. Jets is actively being developed by &lt;a href="https://github.com/tongueroo" rel="noopener noreferrer"&gt;Tung&lt;/a&gt; from &lt;a href="https://www.boltops.com/" rel="noopener noreferrer"&gt;BoltsOps&lt;/a&gt;. From the Jets homepage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ruby on Jets allows you to create and deploy serverless services with ease, and to seamlessly glue AWS services together with the most beautiful dynamic language: Ruby. It includes everything you need to build an API and deploy it to AWS Lambda. Jets leverages the power of Ruby to make serverless joyful for everyone.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h1&gt;
  
  
  What I like about Jets
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Has the same project structure as Rails which makes it easy for those with Rails experience to learn&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rubyonjets.com/docs/deploy/" rel="noopener noreferrer"&gt;Deploys seamlessly to AWS with &lt;code&gt;jets deploy&lt;/code&gt; as long as the machine you run the command on has sufficient permissions&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rubyonjets.com/docs/extras/minimal-deploy-iam/" rel="noopener noreferrer"&gt;A minimum IAM policy for deployment is provided in the docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Every controller action is a deployed as a separate lambda

&lt;ul&gt;
&lt;li&gt;This makes it easy to rewrite specific requests in other languages if needed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://rubyonjets.com/docs/function-properties/" rel="noopener noreferrer"&gt;Lambda function properties are fully customizable at the app, controller, or action level.&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This means request that need more memory/compute can be configured independently&lt;/li&gt;
&lt;li&gt;Access to AWS resources such as S3 can be scoped to just the requests that need them.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://rubyonjets.com/docs/prewarming/" rel="noopener noreferrer"&gt;Prewarming is built in&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://rubyonjets.com/docs/routing/overview/" rel="noopener noreferrer"&gt;routes.rb is automatically translated to API Gateway resources&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;local server and console work the same as &lt;code&gt;rails s&lt;/code&gt; and &lt;code&gt;rails c&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://rubyonjets.com/docs/jobs/" rel="noopener noreferrer"&gt;Scheduled jobs are automatically translated into Cloudwatch Event Rules&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://rubyonjets.com/docs/events/" rel="noopener noreferrer"&gt;Fairly easy to write triggers for lambda&lt;/a&gt;. This along with the previous point eliminates the need for something like DelayedJob.&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://rubyonjets.com/docs/env-extra/" rel="noopener noreferrer"&gt;Ability to deploy extra copies of your app in the same environment&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This can be used in a CI/CD pipeline to deploy the next version of the app and test it against live data before the end users see the changes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  How's performance?
&lt;/h2&gt;

&lt;p&gt;Our staging environment is configured with 512MB of memory. It's deployed in us-east-1 (North Virginia) and I'm in Vancouver, BC. Currently, the request with the longest response time is &lt;code&gt;/login&lt;/code&gt;. After being warmed up (we have prewarming turned off in staging), it takes ~950ms. Keep in mind that an expensive hashing function was chosen intentionally. A more reasonable gauge of performance would be &lt;code&gt;/listings&lt;/code&gt;. This returns the first 20 active listings created by the current authenticated user. The average response time is ~130ms. Shave off 70ms or so for the Vancouver to North Virginia round trip and we're looking at ~40ms compute/DB time. Not the best but certainly good enough for our use cases especially considering we've done no optimization. We'll likely increase the function memory which increases CPU. Our database is also underpowered. Speaking of which...&lt;/p&gt;

&lt;h1&gt;
  
  
  The database
&lt;/h1&gt;

&lt;p&gt;I tried very hard to work with DynamoDB but I just couldn't justify using under current conditions. After a month of part time learning (I was still employed at this time), I decided to go with Postgresql. My rationale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One month of learning about DynamoDB/NoSQL can't compare to nearly a decade of experience working with Postgresql and ActiveRecord&lt;/li&gt;
&lt;li&gt;Time to market &amp;gt;&amp;gt; cost and performance&lt;/li&gt;
&lt;li&gt;If we ever get to the point where Postgresql doesn't scale enough, we have the best problem in the world (too many customers)&lt;/li&gt;
&lt;li&gt;We can use a db.t2.micro (free tier) in staging and Aurora Serverless in production. Hopefully, this means we can set it and forget it in production. Ask me again in six months how this goes.&lt;/li&gt;
&lt;li&gt;If we ever need anything like DynamoDB streams and triggering lambda off of DyanmoDB, we &lt;em&gt;should&lt;/em&gt; be able to get similar results with Kinesis and &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/PostgreSQL-Lambda.html" rel="noopener noreferrer"&gt;Aurora&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;There are many Ruby Gems like &lt;a href="https://github.com/kaminari/kaminari" rel="noopener noreferrer"&gt;Kaminari&lt;/a&gt; for pagination that works with Postgresql/ActiveRecord but not DynamoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Authentication
&lt;/h1&gt;

&lt;p&gt;Any minor mistake implementing authentication can lead to catastrophic results. It's a solved problem and I'm not as smart as the people who already solved it. At this point, I already determined that we would eventually have multiple front ends (web and mobile). Session cookies were a no go. So I started looking for a solution that would manage auth/refresh tokens for me.&lt;/p&gt;

&lt;p&gt;Cognito was the first one I tried. At first glace, it met all my requirements. Cognito would manage all usernames/passwords as well as JWTs. However, I soon realized that Cognito is a forever solution. There is no way to export password hashes out of Cognito. If we ever needed to migrate away from Cognito, we would have to force all users to reset their password.&lt;/p&gt;

&lt;p&gt;In the end, I went with &lt;a href="https://github.com/jeremyevans/rodauth" rel="noopener noreferrer"&gt;rodauth&lt;/a&gt;. Popular solutions like Devise wouldn't work since it's built for Rails. Rodauth on the other hand works with any rack based application. I was pleasantly surprised to discover such as feature rich authentication gem. &lt;a href="https://janko.io/rodauth-a-refreshing-authentication-solution-for-ruby/" rel="noopener noreferrer"&gt;Janko wrote a great article about it using it with Rails&lt;/a&gt;. If there's enough demand I might write an article about using it with Jets.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Full Serverless Dream?
&lt;/h1&gt;

&lt;p&gt;My serverless dream involves setting up the infrastructure once and forgetting about it. &lt;em&gt;Hopefully&lt;/em&gt;, when we get some crazy Black Friday like traffic, Lambda and Aurora can work it's magic and scale without me lifting a finger. Will it work and be seamless for users? Ask me again in 6-12 months. We may not be completely serverless at that point though.&lt;/p&gt;

&lt;p&gt;There's a high chance we'll need some sort of dedicated search service like Elasticsearch in the future. I'm not aware of any serverless solution AWS provides. AWS Elasticsearch Service and Cloudsearch are fully managed services. What's the difference? Fully managed services still require you to configure instance type/size, multi-AZ, and in some cases storage/backup. I'm fairly comfortable working with AWS services and would have no problem configuring them to be highly available and durable but if I can off load this work I will.&lt;/p&gt;

&lt;p&gt;Next week I'll write about why I chose NextJS and TailwindCSS for the frontend.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>aws</category>
      <category>ruby</category>
      <category>serverless</category>
    </item>
    <item>
      <title>I Went Nuts and Quit My Job to Start My Own Company</title>
      <dc:creator>Ivan Wong</dc:creator>
      <pubDate>Tue, 13 Jul 2021 23:21:58 +0000</pubDate>
      <link>https://dev.to/wivarn/i-went-nuts-and-quit-my-job-to-start-my-own-company-204a</link>
      <guid>https://dev.to/wivarn/i-went-nuts-and-quit-my-job-to-start-my-own-company-204a</guid>
      <description>&lt;p&gt;It's been six weeks since I left my job to start a company with my friend &lt;a href="https://www.kevinlegere.com/"&gt;Kevin&lt;/a&gt;. We worked together at &lt;a href="https://www.wegalvanize.com/"&gt;Galvanize&lt;/a&gt;. He was a technical product manager (called product designer at Galvanize) and I was the guy who crushed his dreams down into reality so I could actually build them (aka the developer). We have the same roles in our new company &lt;a href="https://skwirl.io"&gt;skwirl&lt;/a&gt;... or at least we're supposed to. Those of you who have read &lt;a href="https://www.kevinlegere.com/blog"&gt;Kevin's blog&lt;/a&gt; may already know how things have deviated. Those of you who have started your own software companies probably already know that everyone has every role. So what's life been like so far?&lt;/p&gt;

&lt;h2&gt;
  
  
  I've never work so much in my life
&lt;/h2&gt;

&lt;p&gt;I don't actually track my hours but if I had to guess, I'm probably working 80+ hours a week. It's 3AM right now and I'm not sure if I'm going to bed before the sun rises. I've pulled all-nighters several times already. But why am I working so much? Have I gone &lt;em&gt;nuts&lt;/em&gt;? Is Kevin forcing me to work crazy hours? Nope, it's because...&lt;/p&gt;

&lt;h2&gt;
  
  
  I've never had so much fun in my life
&lt;/h2&gt;

&lt;p&gt;Seriously! Working for ourselves and chasing our dreams is amazing. I've never felt so good in my life. Perhaps it's working with a someone I really trust and synergize with. Perhaps it's working in a domain I have a passion for. Perhaps it's taking taking control of our future. Perhaps this is just the honeymoon phase. It's probably all of the above and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Weird things I didn't see coming
&lt;/h2&gt;

&lt;h3&gt;
  
  
  I'm a CFO
&lt;/h3&gt;

&lt;p&gt;It was pretty obvious that I would be in charge of integrating a payment processes (Paypal, Stripe, Square, etc) and managing that account. It turns out I'm also better with money than Kevin is. Next thing I know, I'm opening a business bank account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kevin is my UX dev intern
&lt;/h3&gt;

&lt;p&gt;When I picked Next.js and TailwindCSS for the front end, I knew it was simple enough for Kevin to set up locally to tweak the UI. He is in charge of UX design until we get a real designer. My original plan was to build out the pages and components with most of the styling already done. I knew Kevin would want to tweak the styling once he had the pages rendered in his browser. &lt;a href="https://www.kevinlegere.com/posts/learn-to-code"&gt;He took it to the next level.&lt;/a&gt; The guy who was supposed to the technical product manager is making his own components and making them look good (with some supervision from me). This lead something unexpected...&lt;/p&gt;

&lt;h3&gt;
  
  
  The technical product manager suggested refactoring
&lt;/h3&gt;

&lt;p&gt;What the f**k? In what world does a product manager (or any non-coder) suggest we refactor code? &lt;em&gt;Before we even launch the product?!&lt;/em&gt; About two weeks in, our form components were getting messy and he actually said it.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CpMRUzbE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s14vuzcxhzexnvft0iab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CpMRUzbE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s14vuzcxhzexnvft0iab.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Now you understand why devs want to refactor!&lt;/strong&gt; But Kevin isn't the only one acting weird...&lt;/p&gt;

&lt;h3&gt;
  
  
  The developer (me) has suggested and is totally open to scope creep and moving the deadline earlier
&lt;/h3&gt;

&lt;p&gt;Turns out when the product you're building is your (virtual) baby, scope creep ain't so bad. We've added some extra features that weren't part of our original plan and we're still somehow ahead of schedule. It might have something to do with the lack of meetings, customer requests/complaints, JIRA tickets to fill out, writing tests, and the unhealthy amount of red bull I've consumed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's coming?
&lt;/h2&gt;

&lt;p&gt;If you're reading this blog post, chances are we've already launched our landing page. Over the next few weeks I'll be publishing some more blog posts going over the &lt;em&gt;nuts and bolts&lt;/em&gt; of skwirl:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/lambda/"&gt;AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rubyonjets.com/"&gt;Ruby on Jets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/"&gt;TailwindCSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How can you help?
&lt;/h2&gt;

&lt;p&gt;It's a two skwirl show here. We quit our jobs to chase our dream. If you are interested in what we are doing or share our vision there are a number of ways you can show your support. You can help us spread the word by sharing this post, or any other ones with your network and friends. You can &lt;a href="https://www.buymeacoffee.com/skwirl"&gt;support us on Buy Me a Coffee&lt;/a&gt; which unlocks different perks and rewards such as early access. You can come join the conversation on &lt;a href="https://discord.gg/WHvDqHC2SC"&gt;discord&lt;/a&gt;, &lt;a href="https://www.facebook.com/skwirl.io"&gt;facebook&lt;/a&gt;, &lt;a href="https://twitter.com/Skwirl7"&gt;twitter&lt;/a&gt; or wherever you feel most comfortable.&lt;/p&gt;

&lt;p&gt;We cannot wait to share skwirl with the world. Until then, happy collecting 👋&lt;/p&gt;

</description>
      <category>startup</category>
    </item>
  </channel>
</rss>
