<?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: Taylor Brazelton</title>
    <description>The latest articles on Forem by Taylor Brazelton (@mrbrazel).</description>
    <link>https://forem.com/mrbrazel</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%2F411239%2Fa402ebb4-fb18-46ef-a6ef-3573e7d475e1.jpeg</url>
      <title>Forem: Taylor Brazelton</title>
      <link>https://forem.com/mrbrazel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mrbrazel"/>
    <language>en</language>
    <item>
      <title>Logging Best Practices</title>
      <dc:creator>Taylor Brazelton</dc:creator>
      <pubDate>Tue, 22 Jun 2021 18:57:24 +0000</pubDate>
      <link>https://forem.com/mrbrazel/logging-best-practices-510i</link>
      <guid>https://forem.com/mrbrazel/logging-best-practices-510i</guid>
      <description>&lt;p&gt;Here are just a few bullet points I've gathered over my career about logging.&lt;/p&gt;

&lt;h4&gt;
  
  
  Log to STDOUT/STDERR
&lt;/h4&gt;

&lt;p&gt;Stop following all the online tutorials for your framework that configure your application to store logs directly in files. This is a rookie mistake and makes running your application so much more difficult than it needs to be. Instead, give your ops team better lives by sending your logs to &lt;code&gt;stdout/stderr&lt;/code&gt;. This allows OPs to decide how to process and send off logs. They might choose to write them to disk, perform some cleanup data processing, or simply send logs to an external system. By logging directly to stdout/stderr you give them the choice.&lt;/p&gt;

&lt;p&gt;"A twelve-factor app never concerns itself with routing or storage of its output stream." - &lt;a href="https://12factor.net/logs"&gt;12 Factor App&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Format Your Logs
&lt;/h4&gt;

&lt;p&gt;You don't know how important good, standard formatting can be until you have to sift through multiple log files across different machines. Define a standard formatter and enjoy. I personally like the idea of all logs being provided as JSON dicts. Doing so makes parsing so much easier by logstash. I also, don't have to worry as much about log format changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Centralize your logs(i.e. devops)
&lt;/h4&gt;

&lt;p&gt;This can't be understated. As the software grows so do the sources of your logs. Moving around amonst EC2 instances that log directly to ephemeral storage is a pain. Especially if one of those instances is terminated by autoscaling, which you needed the logs from to help with a customer support ticket. You'll thank me for this tip one day.&lt;/p&gt;

&lt;p&gt;I recommend setting up an ELK stack for this action, or perhaps use a paid tool like datadog.&lt;/p&gt;

&lt;h4&gt;
  
  
  Auto vs Programmer Generated Logs
&lt;/h4&gt;

&lt;p&gt;The importance of having both auto generated logs and programmer logs can't be pushed enough. Auto-generated logs give you insights into everyday events such as GET and POST events to certain api endpoint, meanwhile programmer logs give you the insight into who did what when. The combination of these two types of logs are instrumental to addressing customer service needs. &lt;/p&gt;

&lt;h4&gt;
  
  
  Date-Time Formats
&lt;/h4&gt;

&lt;p&gt;Spitting out datetime values as human-readable seems like a good idea, until you are tasked with parsing through the logs using a datetime range. Use the unambiguous format &lt;a href="https://en.wikipedia.org/wiki/ISO_8601"&gt;ISO-8601&lt;/a&gt;. You'll pat yourself on the back for doing this one, as it makes navigating your logs much easier.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add Logging Context
&lt;/h4&gt;

&lt;p&gt;It's difficult to navigate logs when they have no context. These are three must haves for any SAAS application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tenant ID  - Know the active tenant.&lt;/li&gt;
&lt;li&gt;User ID    - Know the user performing the action.&lt;/li&gt;
&lt;li&gt;Request ID - Usually provided by your Load Balancer. Look at the &lt;code&gt;X-REQUEST-ID&lt;/code&gt; header. Allows you to see all the logs associated with a single request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;At the end of the day, I hope you take logging a bit more seriously. Your customers and users deserve it. Till next time. Peace&lt;/p&gt;

&lt;p&gt;Original Posted on my Personal Blog: &lt;a href="https://taylorbrazelton.com/2021/06/22/2021-06-22-logging-best-practices/"&gt;https://taylorbrazelton.com/2021/06/22/2021-06-22-logging-best-practices/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>logging</category>
      <category>elk</category>
      <category>best</category>
      <category>practices</category>
    </item>
    <item>
      <title>Docker Image Size Reduction</title>
      <dc:creator>Taylor Brazelton</dc:creator>
      <pubDate>Tue, 05 Jan 2021 16:22:58 +0000</pubDate>
      <link>https://forem.com/mrbrazel/docker-image-size-reduction-220a</link>
      <guid>https://forem.com/mrbrazel/docker-image-size-reduction-220a</guid>
      <description>&lt;p&gt;Recently I needed to dockerize an application for a virtual onprem. However, at the end of my project I ended up with a docker image that was 8.4 GB. This is HUGE in terms of size for a container. Thus, I went on a short journey to minimize the image using the following techniques.&lt;/p&gt;

&lt;p&gt;1) Prevent APT from installing recommended packages by adding the following parameter to every apt-get install command: &lt;code&gt;--no-install-recommends&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;2) Added a &lt;code&gt;.dockerignore&lt;/code&gt; file. This allows one to define a list of folders/file globs to ignore when building your Dockerfile.&lt;/p&gt;

&lt;p&gt;3) Reduced the amount of layers by merging related commands.&lt;/p&gt;

&lt;p&gt;4) Clean up apt cache via: &lt;code&gt;&amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;5) Used &lt;a href="https://github.com/wagoodman/dive"&gt;dive&lt;/a&gt; to inspect my image layers to analyze and remove extra unused packages/files.&lt;/p&gt;

&lt;p&gt;6) I stumbled across a tool &lt;a href="https://github.com/docker-slim/docker-slim"&gt;docker-slim&lt;/a&gt; that can supposedly reduce a container's size by up to 30x.&lt;/p&gt;

&lt;p&gt;7) Check your base image. Think about running an alpine version of the container if you haven't already.&lt;/p&gt;

&lt;p&gt;8) Use multistage builds.&lt;/p&gt;

&lt;p&gt;9) Use your layers wisely. Put commands that are more likely to change towards the bottom.&lt;/p&gt;

&lt;p&gt;10) Lint your dockerfile using &lt;a href="https://www.fromlatest.io/"&gt;FromLatest&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sources:&lt;br&gt;
&lt;a href="https://ubuntu.com/blog/we-reduced-our-docker-images-by-60-with-no-install-recommends"&gt;https://ubuntu.com/blog/we-reduced-our-docker-images-by-60-with-no-install-recommends&lt;/a&gt;&lt;br&gt;
&lt;a href="https://towardsdatascience.com/slimming-down-your-docker-images-275f0ca9337e"&gt;https://towardsdatascience.com/slimming-down-your-docker-images-275f0ca9337e&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>image</category>
      <category>size</category>
      <category>reduction</category>
    </item>
    <item>
      <title>AWS Vault and MFA Setup on my Mac</title>
      <dc:creator>Taylor Brazelton</dc:creator>
      <pubDate>Tue, 27 Oct 2020 15:22:57 +0000</pubDate>
      <link>https://forem.com/mrbrazel/aws-vault-and-mfa-setup-on-my-mac-37dk</link>
      <guid>https://forem.com/mrbrazel/aws-vault-and-mfa-setup-on-my-mac-37dk</guid>
      <description>&lt;p&gt;Recently, I decided to secure my local machine by downloading and using AWS Vault. To my surprise, setting up MFA with Vault required a step or two more that I wasn't expecting. Here are my findings/steps. &lt;/p&gt;

&lt;p&gt;First, I used homebrew to install aws-vault.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew cask install aws-vault
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I added my profile and followed the instructions which had me provide my Access ID and Secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws-vault add taylor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I tried to list out the S3 buckets via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws-vault exec taylor -- aws s3 ls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This resulted in the following error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I opened up my AWS config file in Visual Studio Code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code ~/.aws/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing so lead me to see that my profile didn't have a &lt;code&gt;mfa_serial&lt;/code&gt; variable defined. I found my key in IAM under my own user's Security Credentials tab and added it to the file.&lt;/p&gt;

&lt;p&gt;Now, my config file had the following in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[profile taylor]
mfa_serial=arn:aws:iam::XXXXXXXXXX:mfa/taylor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when I performed my s3 listing again, it worked. &lt;/p&gt;

</description>
      <category>mfa</category>
      <category>aws</category>
      <category>vault</category>
    </item>
    <item>
      <title>AlgoliaQB - First Release</title>
      <dc:creator>Taylor Brazelton</dc:creator>
      <pubDate>Fri, 28 Aug 2020 18:21:01 +0000</pubDate>
      <link>https://forem.com/mrbrazel/algoliaqb-first-release-2kfo</link>
      <guid>https://forem.com/mrbrazel/algoliaqb-first-release-2kfo</guid>
      <description>&lt;p&gt;Today I am releasing the first version of Algolia Query Builder. Its purpose is to make generating a filter query easier. You can then pass the generated query directly into the algoliasearch search function. Hopefully, this can help make others' lives easier as well as my own.&lt;/p&gt;

&lt;h4&gt;
  
  
  Installation:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install algoliaqb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Usage Example:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from algoliasearch.search_client import SearchClient
from algoliaqb import AlgoliaQueryBuilder
from flask import request


aqb = AlgoliaQueryBuilder(
    search_param="search",
    filter_map={
        "group_id": "tenant_id",
        "is_reported": "is_reported"
    }
)

# Extract the search query from our flask apps request.args var.
search_query = aqb.get_search_query(request.args)
# Get the filter query from our request args.
filter_query = aqb.get_filter_query(request.args)

# Now that we have the filter string, we just pass it into the search function.

search_client = SearchClient()
index = search_client.init_index("contacts")
results = index.search(search_query, {"filters": filter_query})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information please checkout &lt;a href="https://github.com/Ryanb58/algoliaqb"&gt;Github&lt;/a&gt; or &lt;a href="https://pypi.org/project/algoliaqb/"&gt;Pypi&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>algolia</category>
      <category>query</category>
      <category>builder</category>
      <category>search</category>
    </item>
    <item>
      <title>Minimum Requirements for a SAAS Platform </title>
      <dc:creator>Taylor Brazelton</dc:creator>
      <pubDate>Wed, 22 Jul 2020 02:39:19 +0000</pubDate>
      <link>https://forem.com/mrbrazel/minimum-requirements-for-a-saas-platform-102d</link>
      <guid>https://forem.com/mrbrazel/minimum-requirements-for-a-saas-platform-102d</guid>
      <description>&lt;p&gt;Below is a list of minimum requirements I believe are vital to the success of a SAAS platform(Note that this isn't the word product)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication Strategy(Email, Username, Social, etc..)&lt;/li&gt;
&lt;li&gt;Multi-Tenant Setup&lt;/li&gt;
&lt;li&gt;Database Migrations&lt;/li&gt;
&lt;li&gt;Queuing Mechanism(RabbitMQ, Redis, etc..)&lt;/li&gt;
&lt;li&gt;Caching(Redis, Memcache, etc..)&lt;/li&gt;
&lt;li&gt;Configuration Management&lt;/li&gt;
&lt;li&gt;Search Tools(Elasticsearch, Algolia)&lt;/li&gt;
&lt;li&gt;Unit Tests&lt;/li&gt;
&lt;li&gt;Integration Tests&lt;/li&gt;
&lt;li&gt;Deployment Strategy&lt;/li&gt;
&lt;li&gt;Dockerfied Development&lt;/li&gt;
&lt;li&gt;Dashboard for Statistics(Grafana, Redash, etc..)&lt;/li&gt;
&lt;li&gt;Hosting Provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always try to think through these items before digging into the code. I have found that by doing so you significantly reduce the problems you will run into when trying to scale your product and team.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>requirements</category>
      <category>minimum</category>
      <category>checklist</category>
    </item>
    <item>
      <title>Help Validate My Idea (Seeking Home Owners)</title>
      <dc:creator>Taylor Brazelton</dc:creator>
      <pubDate>Thu, 16 Jul 2020 01:17:17 +0000</pubDate>
      <link>https://forem.com/mrbrazel/help-validate-my-idea-home-owners-2g75</link>
      <guid>https://forem.com/mrbrazel/help-validate-my-idea-home-owners-2g75</guid>
      <description>&lt;p&gt;I am working on generating an idea for SAAS that helps homeowners manage their projects at home. Everything from painting, to replacing one's floor, I feel like there should be some digital book you can use to keep track of these changes. Kind of like GitHub but for your home. Therefore I am looking for a few homeowners to answer some quick questions I have in a survey posted below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forms.gle/PX9jrxAGquRK3qKUA"&gt;https://forms.gle/PX9jrxAGquRK3qKUA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sincerely&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Taylor Brazelton&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>idea</category>
      <category>home</category>
      <category>owner</category>
      <category>house</category>
    </item>
    <item>
      <title>The Bad Multi-Tenant SAAS Pattern</title>
      <dc:creator>Taylor Brazelton</dc:creator>
      <pubDate>Wed, 15 Jul 2020 01:58:51 +0000</pubDate>
      <link>https://forem.com/mrbrazel/the-bad-multi-tenant-saas-pattern-20b4</link>
      <guid>https://forem.com/mrbrazel/the-bad-multi-tenant-saas-pattern-20b4</guid>
      <description>&lt;p&gt;Note: This article assumes PostgreSQL.&lt;/p&gt;

&lt;p&gt;Multi-tenant web apps are currently all the rage. Just take a look at the top tech stocks for July of 2020. Adobe, Salesforce, Microsoft... most if not all of them provide some sort of software as a service. SAAS has taken the web by storm, but there is one specific hidden pattern that is being taught that I MUST ADDRESS.&lt;/p&gt;

&lt;p&gt;The majority of data access strategies for multi-tenant applications are garbage. If you search the web, you'll find lots of articles giving multiple solutions to this problem. Let's go ahead and list the main ones below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared database and shared schema.&lt;/li&gt;
&lt;li&gt;Shared data and separate schemas per tenant.&lt;/li&gt;
&lt;li&gt;Separate databases per tenant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the list above the bottom two are DEAD wrong. You might wonder.. but why Let me dive in deep.&lt;/p&gt;

&lt;h4&gt;
  
  
  First up is database resources.
&lt;/h4&gt;

&lt;p&gt;Since Postgres defines one file on disk per table, you will have a hard limit at the maximum amount of files allowed in a single folder. What is that limit? Heck, do you even know what operating system your database server is even running on? How good is your kernel at handling this madness?&lt;/p&gt;

&lt;h4&gt;
  
  
  Next, lets talk about migrations.
&lt;/h4&gt;

&lt;p&gt;SAAS apps use migrations. It's the easiest way to make changes to the database schema while keeping track of them all. This also forces us, developers, to think more in-depth about our changes since they will go through peer reviews. Imagine having to run a migration on a few of your tenant's databases or schemas.. not horrible but the computational complexity for a multi-database or shame approach raises the migration bar from O(1) to O(n), where &lt;code&gt;n&lt;/code&gt; is the number of tenants you have. Now imagine your app takes off.. grows to a couple of thousand tenants. That's quite the wrench in your CD/CI process. Might as well use the waterfall methodology from this point forward.&lt;/p&gt;

&lt;h4&gt;
  
  
  Issues with Active Records ORMs.
&lt;/h4&gt;

&lt;p&gt;ORMs are a tool that almost every app uses nowadays, they are the essential workers of the SAAS world. ORMs that follow the Active Record pattern have one main thing in common, they all reach out to the database to load up a metadata blob full of them juicy table details. This will dramatically increase your process size when starting up your production servers. Thus unnecessarily bloating your app servers. Say bye-bye to those AWS Dollar bills.&lt;/p&gt;

&lt;h4&gt;
  
  
  Global Reports
&lt;/h4&gt;

&lt;p&gt;Someone from the business side of the house wants a report that contains stats from all of your tenants... Good luck. Who would have realized that you now have duplicate ids across your &lt;code&gt;accounts&lt;/code&gt; tables? Joins be damned.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;Hopefully, I've given you a little insight into why you shouldn't start that multi-tenant app using multiple databases or schemas. Yet if you must, I'd love to know how you solved my list of problems. Please leave me a comment with some feedback. Perhaps I missed a pain-point you had to deal with. Enlighten me.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sources:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.barrons.com/articles/3-software-stocks-that-could-be-big-winners-in-2020-51579005001"&gt;https://www.barrons.com/articles/3-software-stocks-that-could-be-big-winners-in-2020-51579005001&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.lftechnology.com/designing-a-secure-and-scalable-multi-tenant-application-on-node-js-15ae13dda778"&gt;https://blog.lftechnology.com/designing-a-secure-and-scalable-multi-tenant-application-on-node-js-15ae13dda778&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackify.com/writing-multitenant-asp-net-core-applications/"&gt;https://stackify.com/writing-multitenant-asp-net-core-applications/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@venkateshpnk22/how-to-build-a-multi-tenant-application-with-django-rest-framework-part-1-8176d3e315cf"&gt;https://medium.com/@venkateshpnk22/how-to-build-a-multi-tenant-application-with-django-rest-framework-part-1-8176d3e315cf&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/kspeakman/breaking-postgres-with-too-many-tables-4pg0"&gt;https://dev.to/kspeakman/breaking-postgres-with-too-many-tables-4pg0&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>saas</category>
      <category>multitenant</category>
      <category>pattern</category>
      <category>bad</category>
    </item>
  </channel>
</rss>
