<?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: Alex Pliutau</title>
    <description>The latest articles on DEV Community by Alex Pliutau (@der_gopher).</description>
    <link>https://dev.to/der_gopher</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%2F15138%2Fcbbd0b5b-d39f-40d1-ad5f-3a31cb1667b9.png</url>
      <title>DEV Community: Alex Pliutau</title>
      <link>https://dev.to/der_gopher</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/der_gopher"/>
    <language>en</language>
    <item>
      <title>Different ways of working with SQL Databases in Go</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Mon, 23 Sep 2024 09:50:02 +0000</pubDate>
      <link>https://dev.to/der_gopher/different-ways-of-working-with-sql-databases-in-go-2f6g</link>
      <guid>https://dev.to/der_gopher/different-ways-of-working-with-sql-databases-in-go-2f6g</guid>
      <description>&lt;p&gt;&lt;a href="https://itnext.io/different-ways-of-working-with-sql-databases-in-go-9a068441f6f5" rel="noopener noreferrer"&gt;Read the original article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Different programming languages have their own ways of working with relational databases and SQL. Ruby on Rails has its &lt;a href="https://guides.rubyonrails.org/active_record_basics.html" rel="noopener noreferrer"&gt;Active Record&lt;/a&gt;, Python has &lt;a href="https://www.sqlalchemy.org/" rel="noopener noreferrer"&gt;SQLAlchemy&lt;/a&gt;, Typescript — &lt;a href="https://orm.drizzle.team/" rel="noopener noreferrer"&gt;Drizzle&lt;/a&gt;, etc. Go, being a language with quite diverse standard library which includes well-known &lt;a href="https://pkg.go.dev/database/sql" rel="noopener noreferrer"&gt;database/sql&lt;/a&gt; package, has its own libraries and solutions for working with SQL, that suit different needs, preferences and teams.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore and compare most popularly used Go packages with hands-on examples, pros and cons. We will also briefly touch on the topic of database migrations and how to manage them in Go. You’ll get the most out of this article if you already have some experience with Go, SQL and relational databases (doesn’t matter which one).&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Schema
&lt;/h2&gt;

&lt;p&gt;For the purpose of this article, we’ll use a simple schema with three tables: &lt;strong&gt;users, posts and blogs&lt;/strong&gt;. For simplicity we’ll be using &lt;strong&gt;SQLite&lt;/strong&gt; as our database engine, choosing another database engine should not be a problem, as all the libraries we’ll be exploring support multiple SQL dialects.&lt;/p&gt;

&lt;p&gt;Here is our database schema in SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;blogs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;blog_id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blog_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;blogs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is its &lt;a href="https://www.visual-paradigm.com/guide/data-modeling/what-is-entity-relationship-diagram/" rel="noopener noreferrer"&gt;Entity-Relationship Diagram&lt;/a&gt; (ERD):&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Raw SQL and database/sql
&lt;/h2&gt;

&lt;p&gt;Let’s imagine your application needs to perform the following action:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Find the users who have posted at least two articles, along with the total number of posts they’ve made.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In pure SQL that could be translated into the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;post_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="n"&gt;post_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A brief explanation of this query: we JOIN the users and posts tables, then GROUP by user_id, the HAVING clause filters the results to only include users who have posted at least 2 posts, and COUNT aggregates the amount of posts.&lt;/p&gt;

&lt;p&gt;As mentioned above, Go provides a built-in package database/sql with the necessary tools for working with SQL databases. It was developed with simplicity in mind but supports all the necessary functionality such as transactions, parameterized queries, connection pool management, etc.&lt;/p&gt;

&lt;p&gt;As long as you’re comfortable writing your own queries and handling errors and results, it’s a great option. And some would say that it’s the best option, as there is no hidden logic and you can always copy the query and analyze it with EXPLAIN.&lt;/p&gt;

&lt;p&gt;Here is how you can get the results of the query above in Go code using database/sql (some parts like connection is omitted):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;userStats&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;UserName&lt;/span&gt;  &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullString&lt;/span&gt;
  &lt;span class="n"&gt;PostCount&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NullInt64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUsersStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;userStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;`SELECT u.name, COUNT(p.id) AS post_count
FROM users AS u
JOIN posts AS p ON u.id = p.user_id
GROUP BY u.id
HAVING post_count &amp;gt;= ?;`&lt;/span&gt;

  &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;userStats&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;userStats&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the raw SQL query with unnamed parameter, and pass the value of this parameter in conn.Query()&lt;/li&gt;
&lt;li&gt;Iterate over returned rows and manually scan each row into a struct userStats defined above. Note that the struct uses sql.Null* types to handle nullable values properly.&lt;/li&gt;
&lt;li&gt;We need to manually check for possible errors and close the rows to release the resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No additional abstraction/complexity added. Easy to debug raw SQL queries.&lt;/li&gt;
&lt;li&gt;Performance. The database/sql package is quite performant.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provides a good enough abstraction from different database backends.&lt;br&gt;
Cons:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The code becomes a bit verbose as there is a need to scan each row, define proper types and handle errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No compile-time type safety.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the full source for this article in &lt;a href="https://github.com/plutov/packagemain/tree/master/sql-gorm-sqlx-sqlc" rel="noopener noreferrer"&gt;our Github Repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raw SQL and sqlx
&lt;/h2&gt;

&lt;p&gt;Now let’s have a look at some external packages which are popular in Go community.&lt;/p&gt;

&lt;p&gt;If you’re already familiar with database/sql and like its simplicity, you may enjoy working with &lt;a href="https://github.com/jmoiron/sqlx" rel="noopener noreferrer"&gt;sqlx&lt;/a&gt;, which is built on top of standard library and just extends its features. It is very easy to integrate existing codebases using database/sql with sqlx, because it leaves the underlying interfaces such as sql.DB, sql.Tx, etc. untouched.&lt;/p&gt;

&lt;p&gt;The core features of sqlx are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named parameters.&lt;/li&gt;
&lt;li&gt;Easier rows scanning into structs with embedded struct support.&lt;/li&gt;
&lt;li&gt;Better separation between single and multiple rows by using methods Get() and Select().&lt;/li&gt;
&lt;li&gt;Ability to bind a slice of values as a single parameter to an IN query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how you can get the results of the query above using sqlx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;userStats&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;UserName&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`db:"name"`&lt;/span&gt;
  &lt;span class="n"&gt;PostCount&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`db:"post_count"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUsersStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sqlx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;userStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;userStats&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;`SELECT u.name, COUNT(p.id) AS post_count
FROM users AS u
JOIN posts AS p ON u.id = p.user_id
GROUP BY u.id
HAVING post_count &amp;gt;= ?;`&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code we use Select() method which handles scanning of the rows. It also closes the rows automatically so we don’t have to deal with that. The code became much shorter than the database/sql version, but can hide some implementation details from us. For example, be aware that Select loads the whole set into memory at once.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not that different from database/sql. Still easy to debug raw SQL queries.&lt;/li&gt;
&lt;li&gt;A bunch of great features to reduce code verbosity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same as database/sql&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ORMs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Object-relational_mapping" rel="noopener noreferrer"&gt;Object-relational mapping&lt;/a&gt; (ORM) is a technique (some call it a design pattern) of accessing a relational database by working with objects without having to craft complex SQL statements. It is very popular in object-oriented languages, Ruby on Rails has its Active Record, Python has SQLAlchemy, Typescript — Drizzle, etc.&lt;/p&gt;

&lt;p&gt;And Go has &lt;a href="https://github.com/go-gorm/gorm" rel="noopener noreferrer"&gt;GORM&lt;/a&gt;. In a nutshell it lets you write queries as Go code by calling various methods on objects, which are then translated into SQL queries. But not only that, it has other features like database migrations, database resolvers and more.&lt;/p&gt;

&lt;p&gt;You may need to spend a bit of time initially setting up your GORM models, but later it can reduce a lot of boilerplate code.&lt;/p&gt;

&lt;p&gt;Our simple schema and query example may not be the best to visualize the strengths and weaknesses of GORM, but should be enough to demonstrate how we can run a similar query and scan the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;
  &lt;span class="n"&gt;ID&lt;/span&gt;    &lt;span class="kt"&gt;int&lt;/span&gt;
  &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;Posts&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;
  &lt;span class="n"&gt;ID&lt;/span&gt;     &lt;span class="kt"&gt;int&lt;/span&gt;
  &lt;span class="n"&gt;UserID&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;userStats&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;`gorm:"column:post_count"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUsersStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;userStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;userStats&lt;/span&gt;

  &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"COUNT(p.id) AS post_count"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Joins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"JOIN posts AS p ON users.id = p.user_id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users.id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Having&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"post_count &amp;gt;= ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SQL query generated by gorm will be roughly the same as the one we wrote manually in the database/sql variant.&lt;/p&gt;

&lt;p&gt;To summarize the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We declared our User and Post models and extended it with the default gorm.Model struct. Later we can use these two models to build any queries we want by using gorm methods.&lt;/li&gt;
&lt;li&gt;We also defined our small result type userStats&lt;/li&gt;
&lt;li&gt;We used methods such as Select(), Joins(), Group(), Having() to produce the query we want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With such easy example it’s hard to see the potential issues, everything looks just right. However, when your project becomes more complex you will most definitely encounter some issues with that. Just look at StackOverflow questions marked with &lt;a href="https://stackoverflow.com/questions/tagged/go-gorm" rel="noopener noreferrer"&gt;go-gorm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s good to be careful about using ORMs in performance-critical systems or where you need a direct control over database interactions, as gorm uses a lot of reflection and can add overhead and sometimes obscure what’s happening at the database level. Any project where the functionality is wrapped in another huge layer gets the risk of increasing the overall complexity.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abstraction from different database backends.&lt;/li&gt;
&lt;li&gt;Big feature set: migrations, hooks, database resolvers, etc.&lt;/li&gt;
&lt;li&gt;Saves quite a bit of tedious coding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Another layer of complexity and overhead. Hard to debug raw SQL queries.&lt;/li&gt;
&lt;li&gt;Performance drawbacks. May not be as efficient for some critical applications.&lt;/li&gt;
&lt;li&gt;Initial setup can require some time to configure all the models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Generated Go code from SQL using sqlc
&lt;/h2&gt;

&lt;p&gt;This nicely brings us to another unique approach of generating Go code from SQL queries using &lt;a href="https://sqlc.dev/" rel="noopener noreferrer"&gt;sqlc&lt;/a&gt;. With sqlc you write your schema and SQL queries, then use a CLI tool to generate a Go code from it and then use generated code to interact with databases.&lt;/p&gt;

&lt;p&gt;It ensures that your queries are syntactically correct and type-safe and is ideal for those who prefer writing SQL but are looking for an efficient way to integrate it into a Go application.&lt;/p&gt;

&lt;p&gt;sqlc needs to know your database schema and queries in order to generate code, therefore it requires some initial setup. Our schema and query above can be added to the files schema.sql and query.sql and using the following config we can generate the Go code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlite"&lt;/span&gt;
    &lt;span class="na"&gt;queries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query.sql"&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;schema.sql"&lt;/span&gt;
    &lt;span class="na"&gt;gen&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;go&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;
        &lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to name our query in query.sql and mark the parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- name: GetUsersStats :many&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;post_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="n"&gt;post_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we run sqlc generate we can use the following generated types and functions which makes our code type-safe and quite short.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getUsersStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;GetUsersStatsRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUsersStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minPosts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What makes sqlc special is that it understands your database schema, and uses that to validate the SQL you write. So your SQL queries are actually being validated against the actual database table and sqlc will give you a compile-time error if something is wrong.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type safety with generated Go code.&lt;/li&gt;
&lt;li&gt;Still easy to debug SQL code.&lt;/li&gt;
&lt;li&gt;Saves quite a bit of tedious coding.&lt;/li&gt;
&lt;li&gt;Performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial configuration setup for database schema and queries.
Not perfect static analysis. Sometimes you need to explicitly set the parameter type, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re good with SQL statements and prefer not to use much code to express your database interactions, this is your package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Migrations
&lt;/h2&gt;

&lt;p&gt;Since we’re on the topic of SQL databases here, let’s briefly review the database migrations in Go. The schema of the database almost always evolves over time and no one wants to do these changes manually, therefore there are tools developed to help with that.&lt;/p&gt;

&lt;p&gt;The main goal of a database migration tools is to ensure that all environments have the same schema and developers can easily apply the changes or rollback them.&lt;/p&gt;

&lt;p&gt;We mentioned above that GORM can do the migrations as well if you project uses it as ORM. If you use database/sql, sqlx or sqlc you’ll have to use separate projects to manage them.&lt;/p&gt;

&lt;p&gt;The most popular projects are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;golang-migrate — one of the most famous tools for handling database migrations. It supports many database drivers and migration sources, it takes a simple and direct approach for handling database migrations.&lt;/li&gt;
&lt;li&gt;goose — another solid option when choosing a migration tool. It also has support for the main database drivers and one of its main features is support for migrations written in Go and more control of the migrations application process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can then integrate these tools directly into your application or in CI/CD. However, running them properly in CI/CD requires some setup (for example in case of deploying to Kubernetes), and we want to dive deeper into that in our upcoming articles.&lt;/p&gt;

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

&lt;p&gt;There are many well-written, tested and supported database packages for Go that can help with faster development and cleaner code. There is also a very powerful database/sql included into standard library that can do most of your daily work.&lt;/p&gt;

&lt;p&gt;However, whether you should use it or not depends on your needs as a developer, your preferences and your project. In this article, we tried to highlight their strengths and weaknesses.&lt;/p&gt;

&lt;p&gt;You can find the full source for this article in &lt;a href="https://github.com/plutov/packagemain/tree/master/sql-gorm-sqlx-sqlc" rel="noopener noreferrer"&gt;our Github Repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ll end this article with this famous meme:&lt;/p&gt;

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

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://packagemain.tech/p/different-ways-of-working-with-sql" rel="noopener noreferrer"&gt;Read the original article on packagemain.tech&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=41623876" rel="noopener noreferrer"&gt;Discuss this post on Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>sql</category>
      <category>database</category>
    </item>
    <item>
      <title>My first experience with OCaml</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Thu, 19 Sep 2024 11:38:19 +0000</pubDate>
      <link>https://dev.to/der_gopher/my-first-experience-with-ocaml-3d1h</link>
      <guid>https://dev.to/der_gopher/my-first-experience-with-ocaml-3d1h</guid>
      <description>&lt;p&gt;Recently I've been motivated to learn more about functional programming and the name OCaml came up quite a few times. I have seen some praise about it from the people I follow on social media and decided to give it a try. I finally finished a small project in OCaml and would like to share my first impressions while the memory is still fresh.&lt;/p&gt;

&lt;p&gt;Please keep in mind that I am relatively new to functional programming and mainly develop in Go nowadays, previously PHP, Python and Javascript. Therefore I will try not to make any conclusions but rather share my perception.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;I started with the &lt;a href="https://ocaml.org/docs" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;, which was enough to get an overview of the language and its type model, install the development tools and compile a small "hello world" application. However as I dove deeper I discovered other resources as well, such as &lt;a href="https://dev.realworldocaml.org/" rel="noopener noreferrer"&gt;Real World OCaml&lt;/a&gt;, &lt;a href="https://ocamlverse.net/" rel="noopener noreferrer"&gt;OCamlverse&lt;/a&gt;, &lt;a href="https://www.craigfe.io/operator-lookup/" rel="noopener noreferrer"&gt;OCaml Operators&lt;/a&gt; where I could find the information I couldn't find on the official page.&lt;/p&gt;

&lt;p&gt;It all gave me a feeling that there is a need for better-structured resources (especially for newcomers), which should probably live on the official website. Ideally with some interactive step-by-step language tour.&lt;/p&gt;

&lt;h3&gt;
  
  
  Program
&lt;/h3&gt;

&lt;p&gt;My idea was to build a daemon that receives a Yaml configuration with the list of websites and monitors them concurrently, the results are stored in SQLite database afterward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;websites&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://ocaml.org&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://dune.readthedocs.io/en/stable/&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the full source code &lt;a href="https://github.com/plutov/websites_monitoring" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing OCaml
&lt;/h3&gt;

&lt;p&gt;OCaml comes with two compilers: one translating sources into native binaries and another turning sources into a bytecode format. It offers a great runtime performance and has always been exceptionally reliable and stable.&lt;/p&gt;

&lt;p&gt;To start compiling/running our program we need to install the &lt;a href="https://opam.ocaml.org/" rel="noopener noreferrer"&gt;opam&lt;/a&gt; package manager, which also installs the OCaml compiler.&lt;/p&gt;

&lt;p&gt;I am using macos, so the commands for me were the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;opam
opam init
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;opam &lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked without any issues on my machine, and then I was able to install some platform and build tools, such as &lt;a href="https://dune.build/install" rel="noopener noreferrer"&gt;dune&lt;/a&gt; and &lt;a href="https://github.com/ocaml-community/utop" rel="noopener noreferrer"&gt;utop&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opam &lt;span class="nb"&gt;install &lt;/span&gt;dune utop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;utop&lt;/strong&gt; is an interactive REPL which you can run in your terminal. Honestly, I haven't used it much during the development time and just built my program with &lt;strong&gt;dune&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When it comes to &lt;strong&gt;dune&lt;/strong&gt;, I spent some significant time understanding how it works and battling with different dependencies errors. Generally it felt not intuitive to work with multiple &lt;code&gt;dune&lt;/code&gt; files spread across your codebase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dune init proj websites_monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following command created a project directory which looks similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;websites_monitoring/
├── dune-project
├── bin
│   ├── dune
│   └── main.ml
├── lib
│   └── dune
├── &lt;span class="nb"&gt;test&lt;/span&gt;
│   ├── dune
│   └── test_websites_monitoring.ml
└── websites_monitoring.opam
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see there are 3 &lt;code&gt;dune&lt;/code&gt; files, one &lt;code&gt;dune-project&lt;/code&gt; file and one &lt;code&gt;websites_monitoring.opam&lt;/code&gt; file, all of them responsible for building and managing dependencies. It felt like extra work when I had to change &lt;strong&gt;lib&lt;/strong&gt; dependencies, I had to add them to both &lt;code&gt;lib/dune&lt;/code&gt; and &lt;code&gt;dune-project&lt;/code&gt; files as well as run &lt;code&gt;dune build @install&lt;/code&gt; which updates &lt;code&gt;websites_monitoring.opam&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once I added some pseudo code it was time to build an executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dune build
&lt;span class="o"&gt;[&lt;/span&gt;ERROR] No switch is currently set. Please use &lt;span class="s1"&gt;'opam switch'&lt;/span&gt; to &lt;span class="nb"&gt;set &lt;/span&gt;or &lt;span class="nb"&gt;install &lt;/span&gt;a switch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This didn't work for me because there was no default switch installed, even though the documentation say that there should be one. &lt;a href="https://ocaml.org/docs/opam-switch-introduction" rel="noopener noreferrer"&gt;Switches&lt;/a&gt; are isolated OCaml environments, similar to Python's &lt;code&gt;virtualenv&lt;/code&gt;. I didn't dive into this much and just chose some switch from the list of available options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opam switch create ocaml-base-compiler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I was able to compile and run my program, the binary is located in &lt;code&gt;./_build/install/default/bin/monitoring&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All in all, it seems that &lt;strong&gt;dune&lt;/strong&gt; is an interesting tool, but I also think it needs a better documentation and simplified developer experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Language Syntax and Types
&lt;/h3&gt;

&lt;p&gt;OCaml is type-safe and statically type language, which means it detects type errors at compile time. But what's cool is that there is a type inference, so you do not have to write type information everywhere. The compiler automatically figures out most types. This can make the code easier to read and maintain.&lt;/p&gt;

&lt;p&gt;For example in my code I have this function to get a config filename from the environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;get_config_filename&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt; &lt;span class="s2"&gt;"CONFIG"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Not_found&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;"./websites.yaml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could omit &lt;code&gt;: string&lt;/code&gt; part and compiler still understands that it returns a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;get_config_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c"&gt;(* ... *)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are user-defined types in OCaml such as variants, records and aliases. Here is the example of custom website type that corresponds to the yaml configuration of my program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;websites&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The language has lots of operators, and it took me some time to understand how they work. This &lt;a href="https://www.craigfe.io/operator-lookup/" rel="noopener noreferrer"&gt;webpage&lt;/a&gt; helped me a lot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preprocessors and PPXs
&lt;/h3&gt;

&lt;p&gt;Preprocessors are programs meant to be called at compile time, so that they alter the program before the actual compilation.&lt;/p&gt;

&lt;p&gt;In my case they've been really helpful to decode a Yaml file into a custom type after I spent few hours to do the same without them. I used &lt;a href="https://github.com/patricoferris/ppx_deriving_yaml" rel="noopener noreferrer"&gt;ppx_deriving_yaml&lt;/a&gt; for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;deriving&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;websites&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;deriving&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;get_websites_from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_filename&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Yaml_unix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;of_file&lt;/span&gt; &lt;span class="nn"&gt;Fpath&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="n"&gt;config_filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;yaml_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="nt"&gt;`Msg&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Invalid_argument&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Unable to open/parse yaml file: "&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;in&lt;/span&gt;

  &lt;span class="c"&gt;(* preprocessors are doing the magic of making websites_of_yaml *)&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;websites_of_yaml&lt;/span&gt; &lt;span class="n"&gt;yaml_value&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;websites&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="nt"&gt;`Msg&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Invalid_argument&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid config format: "&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Concurrency &amp;amp; Parallelism
&lt;/h3&gt;

&lt;p&gt;Since I have the list of websites I wanted to process each website concurrently with some interval between runs. I couldn't use &lt;code&gt;Domains&lt;/code&gt; for that, because the amount of websites could be larger than the amount of cores available.&lt;/p&gt;

&lt;p&gt;It seems there are &lt;a href="https://ocamlverse.net/content/parallelism.html" rel="noopener noreferrer"&gt;many libraries&lt;/a&gt; to implement concurrency or parallelism in OCaml and it's not obvious what to use. I think that it's the case with OCaml in general, the stdlib is quite minimal and there are many small independent libraries. Which is not an issue maybe, but hard to navigate especially for newcomers.&lt;/p&gt;

&lt;p&gt;So I went with &lt;a href="https://github.com/ocaml-multicore/domainslib" rel="noopener noreferrer"&gt;domainslib&lt;/a&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;rec&lt;/span&gt; &lt;span class="n"&gt;run_async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c"&gt;(* crawl website and store the result in the db *)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Crawler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crawl_website&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="nn"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert_website&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;(* wait for the next interval *)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Unix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="n"&gt;run_async&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_config_filename&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_websites_from_file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="nn"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Websites found in config: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;num_domains&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recommended_domain_count&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="nn"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Number of domains/cores: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="n"&gt;num_domains&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;(* create a pool and process each website concurrently *)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup_pool&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;num_domains&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_domains&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="nn"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_async&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
      &lt;span class="bp"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nn"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;teardown_pool&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;print_endline&lt;/span&gt; &lt;span class="s2"&gt;"Exiting..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I actually spent a lot of time battling the &lt;code&gt;segmentation fault&lt;/code&gt; error because the &lt;code&gt;crawl_website&lt;/code&gt; function used &lt;code&gt;cohttp-lwt&lt;/code&gt; under the hood. After I switched crawl_website to be synchronous everything worked well.&lt;/p&gt;

&lt;p&gt;It was actually hard to find a library that does synchronous HTTP calls, everything uses LWT or Async.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;There seem to be different ways of defining your tests, but I went with a simple approach of running the tests manually in let():&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;Monitoring&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;test_get_websites_from_file&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_websites_from_file&lt;/span&gt; &lt;span class="s2"&gt;"test_websites.yaml"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hd&lt;/span&gt; &lt;span class="n"&gt;websites&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://ocaml.org"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nn"&gt;Unix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chdir&lt;/span&gt; &lt;span class="s2"&gt;"../../../test/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;test_get_websites_from_file&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I could run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dune runtest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Containerization
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ocaml/opam&lt;/code&gt; Docker image works well and I was able to quickly put my application into a container. I don't plan to deploy this app, so didn't worry much about multi-stage builds for now, just wanted to make sure it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ocaml/opam:alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;init-opam&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apk add &lt;span class="nt"&gt;--update&lt;/span&gt; gmp-dev sqlite-dev linux-headers openssl-dev curl-dev libcurl curl

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;init-opam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ocaml-app-base&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/opam/websites_monitoring&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;opam &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--deps-only&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;opam &lt;span class="nb"&gt;install &lt;/span&gt;dune
&lt;span class="k"&gt;RUN &lt;/span&gt;opam &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; dune build

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/home/opam/websites_monitoring/_build/install/default/bin/monitoring"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Will I be confident deploying this program to production? Probably not. But it kinda works. I would like to understand the domainslib better and maybe try few other approaches. And probably run some performance tests.&lt;/p&gt;

&lt;p&gt;Was my journey learning OCaml and doing first steps easy? Probably not. I wish the documentation was easily accessible for the newcomers and structured better. Ideally in one place.&lt;/p&gt;

&lt;p&gt;But definitely it was enjoyable and I am 100% sure I will be continuing playing with OCaml and maybe publish some libraries.&lt;/p&gt;

&lt;p&gt;You can find the full source code &lt;a href="https://github.com/plutov/websites_monitoring" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pliutau.com/my-first-experience-with-ocaml/" rel="noopener noreferrer"&gt;Discuss this post on pliutau.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discuss.ocaml.org/t/my-first-experience-with-ocaml/15297" rel="noopener noreferrer"&gt;Discuss this post on OCaml Community Forum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=41568762" rel="noopener noreferrer"&gt;Discuss this post on Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/pliutau/status/1836067286303617237" rel="noopener noreferrer"&gt;Discuss this post on X&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ocaml</category>
      <category>dune</category>
      <category>functional</category>
    </item>
    <item>
      <title>Rust for Gophers</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Thu, 18 Jul 2024 13:24:57 +0000</pubDate>
      <link>https://dev.to/der_gopher/rust-for-gophers-1d99</link>
      <guid>https://dev.to/der_gopher/rust-for-gophers-1d99</guid>
      <description>&lt;p&gt;&lt;a href="https://packagemain.tech/p/rust-for-gophers" rel="noopener noreferrer"&gt;Read the original article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Should you Rust, or should you Go? Which language is better, and does that question even make sense? Let’s talk about Go versus Rust in 2024, with our special guest, John Arundel. John is the author of For the Love of Go, Cloud Native DevOps with Kubernetes, and many other books. He also teaches both Go and Rust, so it’ll be interesting to hear his perspective. Here’s the interview!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;John, we seem to be hearing more and more about Rust these days. Can you give us a quick introduction? What is Rust for, and why does it exist?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sure, that’s easy. Rust is a language for controlling elevators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you kidding?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not at all. Graydon Hoare, the originator of Rust, got frustrated when the elevators in his building kept breaking down due to software problems. He thought “Surely we can do better than this!” And the rest is history.&lt;/p&gt;

&lt;p&gt;We can’t prevent all bugs, but we can at least use a programming language that eliminates some major categories of bugs, such as buffer overflows, data races, and “use after free” issues. So from the very beginning, Rust’s focus has been on building reliable software, automating many of the safety checks that good programmers do anyway, and helping to catch mistakes before they reach production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That sounds like a good idea, but I get the impression that Rust is a hard language to learn—especially compared to Go. Is that fair, and if so, why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go and Rust are both trying, in different ways, to solve the same problem: it’s hard to write software at scale in traditional languages like C and C++, because the programmers have to be very experienced and knowledgeable to avoid making mistakes. Even experts can slip up from time to time, and it takes a long time to train programmers to become experts.&lt;/p&gt;

&lt;p&gt;Go tackles this problem by radically simplifying the language: compared to something like C++, it has a lot less syntax to learn. So programmers can spend their time learning to write programs well, rather than mastering a big, complex language. Go is lean, but effective.&lt;/p&gt;

&lt;p&gt;To use an analogy, it’s easier to learn to drive a tractor than to fly a spaceship. The tractor may be a humble, pragmatic machine, but it does its job perfectly, and it’s actually a better choice than the spaceship for many tasks: ploughing a field, for example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I like your analogy. I guess Rust is the spaceship?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, Rust is big, complicated, and powerful, combining many of the best ideas from traditional imperative languages like C with functional programming concepts borrowed from languages like Haskell and Lisp.&lt;/p&gt;

&lt;p&gt;There’s more to learn in Rust than there is in Go, but then it does more! If you want to fly to Mars, a spaceship is a better choice than a tractor. Of course, it takes a little longer to train an astronaut than a tractor driver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go has built-in garbage collection, which is great for simplicity. How does memory management work in Rust, and is it a big challenge to learn?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, garbage collection means you don’t have to worry about allocating and freeing memory yourself, as you do in languages like C++. That makes programming easier and eliminates all sorts of memory-related bugs. On the other hand, you need a relatively complex runtime, and garbage collection affects performance.&lt;/p&gt;

&lt;p&gt;Rust takes a different approach. It reclaims memory automatically, but without having to pause the program. It can do this by keeping track of all the references to a particular piece of data that exist. When no part of the program can refer to the data any more, Rust knows that bit of memory can be safely recycled straight away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes, I’ve heard Rust has a strong focus on ownership and borrowing. How do these concepts compare to working with pointers in Go, and what are some good ways to wrap my head around them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, I have good news—if you’re already used to pointers in Go, then references in Rust work basically the same way, only safer. If you create a mutable reference to a variable, it works just like a Go pointer: you can pass it to a function, or store it somewhere.&lt;/p&gt;

&lt;p&gt;But, unlike in Go, as long as that mutable reference exists, it has exclusive access to the data: no one else can modify it, or even read it. In Go terms, it’s like having an automatic mutex lock. And when a function doesn’t need to modify the data, it can instead borrow a shared reference, which is read-only, and lots of them can exist at once.&lt;/p&gt;

&lt;p&gt;Rust also keeps track of the original data: when it goes out of scope, any references to it are no longer valid. So the compiler can detect many kinds of dangling pointer bugs where you try to use a reference to a value that doesn’t exist any more. That results in undefined behaviour, which is a nice way of saying that something horrible will happen, and part of Rust’s value proposition is “no undefined behaviour—ever”.&lt;/p&gt;

&lt;p&gt;In Rust, then, we have to figure out a way to write our programs so that references to data are always valid, and only one mutable reference ever exists at a time. That takes some getting used to (Rust programmers call it “fighting the borrow checker”), but the resulting programs are more reliable, and more likely to be correct.&lt;/p&gt;

&lt;p&gt;For example, all Go programmers are familiar with data races, where two or more goroutines try to access some shared data at the same time, with unpredictable results: at best, the program will crash, and at worst, it will continue with corrupted or invalid data.&lt;/p&gt;

&lt;p&gt;In Rust, a program like this won’t compile! The ownership and reference rules mean that two mutable references to the same thing can’t exist simultaneously. You just have to solve the problem a different way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That brings us neatly on to concurrency. I like Go’s concurrency features with channels and goroutines. How does Rust handle concurrency, and are there any similarities I can leverage from my Go experience?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, goroutines and channels are great: a super-lightweight task abstraction that’s very cheap compared to traditional multithreading. On the other hand, Go only gives us the fundamental building blocks: it’s up to us to make sure we use them safely, avoiding data races or deadlocks. And that can be hard to do!&lt;/p&gt;

&lt;p&gt;Rust doesn’t have goroutines, but it does have async tasks, which are much like goroutines, only with the usual Rust safety guarantees. There are also some excellent third-party frameworks such as Tokio and Rayon that can just take a bunch of data and automatically figure out the most efficient way to crunch it in parallel.&lt;/p&gt;

&lt;p&gt;So, while concurrent programs will always be difficult to write well, if you can do it in Go, you’ll find those skills transfer well to Rust, too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I like to learn by doing. Are there any good hands-on exercises or projects you’d recommend for a Go programmer getting started with Rust, like the Tour of Go, for example?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rustlings is a great place to start: it’s a set of interactive bite-size exercises that guide you through all the language fundamentals. If you want to get feedback from a real live human, check out the Rust track on Exercism. There’s also Rust by Example, which is a terrific resource for working example snippets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It sounds like you’re a fan of both languages. Which do you prefer, and would you recommend that someone who already knows Go should also try learning Rust?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, Go and Rust each appeal to different parts of my brain. I like the radical simplicity and pragmatism of Go: it does a lot with very little, and solves most problems pretty well.&lt;/p&gt;

&lt;p&gt;Rust, on the other hand, neatly fills the gaps where Go isn’t an ideal choice: kernels, firmware, embedded devices, and safety-critical applications such as medical devices, industry, aerospace, and so on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And elevators, of course.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Naturally! So I like that aspect of Rust, and it’s also just a really fun and expressive language. I think it’s well worth anyone’s time to have a play with Rust, and stick with it long enough to get over the initial unfamiliarity of the syntax, and the struggles with the borrow checker.&lt;/p&gt;

&lt;p&gt;Even if you decide that Rust isn’t for you, you’ll learn some interesting new ways of thinking about problems, and it also helps you understand more about the different trade-offs that Go makes.&lt;/p&gt;

&lt;p&gt;From a career point of view, both Go and Rust will be very valuable skills for the foreseeable future. I think that pretty soon, instead of “Go versus Rust”, we’ll be talking about “Go and Rust versus everything else.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;John, thanks for being our guest, and giving us your perspective on Go and Rust. Where can people find out more about you—for example, if they’re interested in your books or training courses?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s my pleasure! Do drop by my website at bitfieldconsulting.com if you’d like to know more, or get in touch—I’ll be happy to chat.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>go</category>
    </item>
    <item>
      <title>Mastering SOLID Principles with Go Examples</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Tue, 09 Jul 2024 06:26:36 +0000</pubDate>
      <link>https://dev.to/der_gopher/mastering-solid-principles-with-go-examples-13pf</link>
      <guid>https://dev.to/der_gopher/mastering-solid-principles-with-go-examples-13pf</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foylzvabb8ecqrf8eu3gi.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foylzvabb8ecqrf8eu3gi.jpeg" alt="solid" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The SOLID principles are a set of design guidelines that help developers write more maintainable, scalable, and testable code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/mastering-solid-principles-with-go" rel="noopener noreferrer"&gt;Read the full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>softwaredesign</category>
    </item>
    <item>
      <title>Automating Semantic Versioning with Github Actions and Branch Naming Conventions</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Mon, 08 Jul 2024 20:37:47 +0000</pubDate>
      <link>https://dev.to/der_gopher/automating-semantic-versioning-with-github-actions-and-branch-naming-conventions-mpa</link>
      <guid>https://dev.to/der_gopher/automating-semantic-versioning-with-github-actions-and-branch-naming-conventions-mpa</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxtst2qof4hvmy16n6ww.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxtst2qof4hvmy16n6ww.jpeg" alt="git flow" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/github-actions-semver" rel="noopener noreferrer"&gt;Read the full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
      <category>git</category>
    </item>
    <item>
      <title>Automating Semantic Versioning with Github Actions and Branch Naming Conventions</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Mon, 01 Jul 2024 12:16:28 +0000</pubDate>
      <link>https://dev.to/der_gopher/automating-semantic-versioning-with-github-actions-and-branch-naming-conventions-33oh</link>
      <guid>https://dev.to/der_gopher/automating-semantic-versioning-with-github-actions-and-branch-naming-conventions-33oh</guid>
      <description>&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%2Fsou5j3ep5oqg93woeiw4.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsou5j3ep5oqg93woeiw4.jpeg" alt="semver"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/github-actions-semver" rel="noopener noreferrer"&gt;Read the full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>semver</category>
    </item>
    <item>
      <title>Rust for Gophers</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Fri, 28 Jun 2024 07:31:41 +0000</pubDate>
      <link>https://dev.to/der_gopher/rust-for-gophers-k9h</link>
      <guid>https://dev.to/der_gopher/rust-for-gophers-k9h</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcx4dcmv4n51nmvvng39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcx4dcmv4n51nmvvng39.png" alt="rust-vs-go" width="744" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/rust-for-gophers" rel="noopener noreferrer"&gt;Read full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>go</category>
    </item>
    <item>
      <title>Performance Benchmarking: gRPC+Protobuf vs. HTTP+JSON</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Mon, 17 Jun 2024 11:03:42 +0000</pubDate>
      <link>https://dev.to/der_gopher/performance-benchmarking-grpcprotobuf-vs-httpjson-2jck</link>
      <guid>https://dev.to/der_gopher/performance-benchmarking-grpcprotobuf-vs-httpjson-2jck</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gd2xfcoile0npt1oyje.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gd2xfcoile0npt1oyje.jpeg" alt="vs" width="800" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/protobuf-grpc-vs-json-http" rel="noopener noreferrer"&gt;Read full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>grpc</category>
      <category>performance</category>
      <category>json</category>
    </item>
    <item>
      <title>Bridging Backend and Data Engineering: Communicating Through Events</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Fri, 14 Jun 2024 08:00:04 +0000</pubDate>
      <link>https://dev.to/der_gopher/bridging-backend-and-data-engineering-communicating-through-events-33pn</link>
      <guid>https://dev.to/der_gopher/bridging-backend-and-data-engineering-communicating-through-events-33pn</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66awd1frfybv88ztw89h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66awd1frfybv88ztw89h.jpg" alt="diagram" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/bridging-backend-and-data-engineering" rel="noopener noreferrer"&gt;Read the full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>pubsub</category>
      <category>backend</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>The Developer's Guide to Database Proxies</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Mon, 10 Jun 2024 09:42:23 +0000</pubDate>
      <link>https://dev.to/der_gopher/the-developers-guide-to-database-proxies-ak0</link>
      <guid>https://dev.to/der_gopher/the-developers-guide-to-database-proxies-ak0</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs23l89hyi11axx8awakv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs23l89hyi11axx8awakv.jpg" alt="diagram" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/the-developers-guide-to-database" rel="noopener noreferrer"&gt;Read full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>distributedsystems</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Identifying Container Image Vulnerabilities with Docker Scout</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Sun, 09 Jun 2024 13:39:54 +0000</pubDate>
      <link>https://dev.to/der_gopher/identifying-container-image-vulnerabilities-with-docker-scout-503o</link>
      <guid>https://dev.to/der_gopher/identifying-container-image-vulnerabilities-with-docker-scout-503o</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kcgwo2gueldysu9l05q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kcgwo2gueldysu9l05q.jpeg" alt="diagram" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/identifying-container-image-vulnerabilities" rel="noopener noreferrer"&gt;Read the full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>security</category>
      <category>cicd</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Unified Cache Keys: How Namespaced Keys Improve Service Interoperability</title>
      <dc:creator>Alex Pliutau</dc:creator>
      <pubDate>Sun, 09 Jun 2024 13:36:57 +0000</pubDate>
      <link>https://dev.to/der_gopher/unified-cache-keys-how-namespaced-keys-improve-service-interoperability-2p2c</link>
      <guid>https://dev.to/der_gopher/unified-cache-keys-how-namespaced-keys-improve-service-interoperability-2p2c</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F653484m9d6f6x0m78sav.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F653484m9d6f6x0m78sav.jpg" alt="diagram" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packagemain.tech/p/unified-namespaced-cache-keys" rel="noopener noreferrer"&gt;Read the full article on packagemain.tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>redis</category>
      <category>systemdesign</category>
      <category>microservices</category>
      <category>distributedsystems</category>
    </item>
  </channel>
</rss>
