<?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: Talo Oyweka</title>
    <description>The latest articles on DEV Community by Talo Oyweka (@talo_oyweka_d7847a162c1ad).</description>
    <link>https://dev.to/talo_oyweka_d7847a162c1ad</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940256%2Fd6cee470-fa11-43de-94ef-8407d58d3015.png</url>
      <title>DEV Community: Talo Oyweka</title>
      <link>https://dev.to/talo_oyweka_d7847a162c1ad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/talo_oyweka_d7847a162c1ad"/>
    <language>en</language>
    <item>
      <title>Relational Data Modelling for a Forum in Go.</title>
      <dc:creator>Talo Oyweka</dc:creator>
      <pubDate>Fri, 24 Jul 2026 09:27:44 +0000</pubDate>
      <link>https://dev.to/talo_oyweka_d7847a162c1ad/relational-data-modelling-for-a-forum-in-go-39hn</link>
      <guid>https://dev.to/talo_oyweka_d7847a162c1ad/relational-data-modelling-for-a-forum-in-go-39hn</guid>
      <description>&lt;p&gt;Building a forum backend in Go with SQLite is a great way to keep the architecture simple without sacrificing clarity. The main challenge is designing a schema that stays normalized, mapping it cleanly into Go structs, and configuring database access so concurrent posts do not trigger lock errors. A good design also makes tagging easy to query and maintain, instead of turning tags into an awkward text blob.&lt;/p&gt;

&lt;p&gt;Forum data model&lt;br&gt;
A forum usually revolves around a few core entities: users, categories, posts, comments, likes, and tags. Each entity should have its own table, while relationships should be represented with foreign keys or junction tables. This keeps the data consistent, avoids duplication, and makes querying easier over time.&lt;/p&gt;

&lt;p&gt;A practical structure is:&lt;/p&gt;

&lt;p&gt;users for account identity.&lt;/p&gt;

&lt;p&gt;categories for grouping posts into topics.&lt;/p&gt;

&lt;p&gt;posts for the main discussion content.&lt;/p&gt;

&lt;p&gt;comments for replies and threaded conversations.&lt;/p&gt;

&lt;p&gt;likes for user reactions.&lt;/p&gt;

&lt;p&gt;tags for flexible labels.&lt;/p&gt;

&lt;p&gt;post_tags for the many-to-many relationship between posts and tags.&lt;/p&gt;

&lt;p&gt;This structure reflects standard relational design principles and keeps the schema flexible as the forum grows.&lt;/p&gt;

&lt;p&gt;SQLite schema design&lt;br&gt;
SQLite works well for this kind of application because it is lightweight, embedded, and easy to ship. The key is to use the database the way it wants to be used: normalized tables, foreign keys, and carefully chosen indexes. For a forum, the schema should preserve referential integrity so that deleting a post can cascade to its comments and tag links if you want that behavior.&lt;/p&gt;

&lt;p&gt;A solid starting point looks like this:&lt;/p&gt;

&lt;p&gt;sql&lt;br&gt;
PRAGMA foreign_keys = ON;&lt;/p&gt;

&lt;p&gt;CREATE TABLE users (&lt;br&gt;
  id INTEGER PRIMARY KEY AUTOINCREMENT,&lt;br&gt;
  username TEXT NOT NULL UNIQUE,&lt;br&gt;
  email TEXT NOT NULL UNIQUE,&lt;br&gt;
  created_at TEXT NOT NULL DEFAULT (datetime('now'))&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;CREATE TABLE categories (&lt;br&gt;
  id INTEGER PRIMARY KEY AUTOINCREMENT,&lt;br&gt;
  name TEXT NOT NULL UNIQUE,&lt;br&gt;
  slug TEXT NOT NULL UNIQUE&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;CREATE TABLE posts (&lt;br&gt;
  id INTEGER PRIMARY KEY AUTOINCREMENT,&lt;br&gt;
  user_id INTEGER NOT NULL,&lt;br&gt;
  category_id INTEGER NOT NULL,&lt;br&gt;
  title TEXT NOT NULL,&lt;br&gt;
  body TEXT NOT NULL,&lt;br&gt;
  created_at TEXT NOT NULL DEFAULT (datetime('now')),&lt;br&gt;
  updated_at TEXT,&lt;br&gt;
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,&lt;br&gt;
  FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE RESTRICT&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;CREATE TABLE comments (&lt;br&gt;
  id INTEGER PRIMARY KEY AUTOINCREMENT,&lt;br&gt;
  post_id INTEGER NOT NULL,&lt;br&gt;
  user_id INTEGER NOT NULL,&lt;br&gt;
  parent_comment_id INTEGER,&lt;br&gt;
  body TEXT NOT NULL,&lt;br&gt;
  created_at TEXT NOT NULL DEFAULT (datetime('now')),&lt;br&gt;
  FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,&lt;br&gt;
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,&lt;br&gt;
  FOREIGN KEY (parent_comment_id) REFERENCES comments(id) ON DELETE CASCADE&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;CREATE TABLE likes (&lt;br&gt;
  user_id INTEGER NOT NULL,&lt;br&gt;
  post_id INTEGER NOT NULL,&lt;br&gt;
  created_at TEXT NOT NULL DEFAULT (datetime('now')),&lt;br&gt;
  PRIMARY KEY (user_id, post_id),&lt;br&gt;
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,&lt;br&gt;
  FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;CREATE TABLE tags (&lt;br&gt;
  id INTEGER PRIMARY KEY AUTOINCREMENT,&lt;br&gt;
  name TEXT NOT NULL UNIQUE,&lt;br&gt;
  slug TEXT NOT NULL UNIQUE&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;CREATE TABLE post_tags (&lt;br&gt;
  post_id INTEGER NOT NULL,&lt;br&gt;
  tag_id INTEGER NOT NULL,&lt;br&gt;
  PRIMARY KEY (post_id, tag_id),&lt;br&gt;
  FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,&lt;br&gt;
  FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE&lt;br&gt;
);&lt;br&gt;
The likes and post_tags tables both use composite primary keys because each pair should only appear once. That avoids duplicate likes and duplicate tag links while keeping the schema compact. This is a classic use case for a junction table in relational databases.&lt;/p&gt;

&lt;p&gt;Indexes and integrity&lt;br&gt;
Foreign keys alone are not enough for performance. You also want indexes on the columns used most often in joins and filters, especially posts.user_id, posts.category_id, comments.post_id, and post_tags.tag_id. Without those indexes, even a modest forum can become slow when listing posts by category or loading tags for a page.&lt;/p&gt;

&lt;p&gt;A few useful indexes are:&lt;/p&gt;

&lt;p&gt;sql&lt;br&gt;
CREATE INDEX idx_posts_user_id ON posts(user_id);&lt;br&gt;
CREATE INDEX idx_posts_category_id ON posts(category_id);&lt;br&gt;
CREATE INDEX idx_comments_post_id ON comments(post_id);&lt;br&gt;
CREATE INDEX idx_comments_user_id ON comments(user_id);&lt;br&gt;
CREATE INDEX idx_post_tags_tag_id ON post_tags(tag_id);&lt;br&gt;
If you plan to search by tag name often, the unique index created by UNIQUE(name) on tags already helps. Keeping PRAGMA foreign_keys = ON enabled is also important because SQLite does not enforce foreign keys unless explicitly told to do so. That makes the difference between a resilient schema and one that quietly accumulates broken references.&lt;/p&gt;

&lt;p&gt;Mapping to Go structs&lt;br&gt;
Once the schema is settled, the next step is matching it with Go types. The cleanest approach is to keep one struct per table and then add optional nested fields for joined data. You generally want to keep raw foreign key IDs in the struct so that inserts and updates stay simple.&lt;/p&gt;

&lt;p&gt;A straightforward model might look like this:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
type User struct {&lt;br&gt;
    ID        int64&lt;br&gt;
    Username  string&lt;br&gt;
    Email     string&lt;br&gt;
    CreatedAt time.Time&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;type Category struct {&lt;br&gt;
    ID   int64&lt;br&gt;
    Name string&lt;br&gt;
    Slug string&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;type Tag struct {&lt;br&gt;
    ID   int64&lt;br&gt;
    Name string&lt;br&gt;
    Slug string&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;type Post struct {&lt;br&gt;
    ID         int64&lt;br&gt;
    UserID     int64&lt;br&gt;
    CategoryID int64&lt;br&gt;
    Title      string&lt;br&gt;
    Body       string&lt;br&gt;
    CreatedAt  time.Time&lt;br&gt;
    UpdatedAt  *time.Time&lt;br&gt;
    Author     *User&lt;br&gt;
    Category   *Category&lt;br&gt;
    Tags       []Tag&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;type Comment struct {&lt;br&gt;
    ID              int64&lt;br&gt;
    PostID          int64&lt;br&gt;
    UserID          int64&lt;br&gt;
    ParentCommentID *int64&lt;br&gt;
    Body            string&lt;br&gt;
    CreatedAt       time.Time&lt;br&gt;
    Author          *User&lt;br&gt;
    Replies         []Comment&lt;br&gt;
}&lt;br&gt;
This pattern gives you the best of both worlds. The base struct mirrors the table and is easy to persist, while nested fields are filled when you load joined data for views or API responses. It also avoids overcomplicating the database layer with an ORM-style object graph that does not match the actual schema.&lt;/p&gt;

&lt;p&gt;Loading data cleanly&lt;br&gt;
For most forum pages, you will query data in layers. For example, when loading a post page, you may fetch the post row first, then load comments, tags, author details, and category details. This approach keeps the SQL readable and gives you control over performance.&lt;/p&gt;

&lt;p&gt;For tags, the usual query looks like this:&lt;/p&gt;

&lt;p&gt;sql&lt;br&gt;
SELECT t.id, t.name, t.slug&lt;br&gt;
FROM tags t&lt;br&gt;
JOIN post_tags pt ON pt.tag_id = t.id&lt;br&gt;
WHERE pt.post_id = ?;&lt;br&gt;
For comments, you may want one query for the top-level comments and another for replies, or you may load all comments for a post and build the tree in Go. The latter is often easier if you want nested threads. A forum backend usually benefits from explicit SQL and explicit mapping rather than trying to hide all of the relationships behind magic.&lt;/p&gt;

&lt;p&gt;Concurrency in Go&lt;br&gt;
SQLite can handle many readers, but it allows only one writer at a time. That is why forum applications can hit database is locked errors when several users post simultaneously. The solution is not to fight SQLite with lots of connections; it is to configure the connection pool in a way that fits SQLite’s locking model.&lt;/p&gt;

&lt;p&gt;Go’s sql.DB is designed as a concurrency-safe handle with its own internal connection pool. The handle itself is safe to share among goroutines, and the package creates and reuses connections as needed. For SQLite, however, you usually want to keep the number of open connections low, often to one, because multiple concurrent writers create contention rather than throughput.&lt;/p&gt;

&lt;p&gt;A common configuration looks like this:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
db, err := sql.Open("sqlite3", "file:forum.db?_busy_timeout=5000&amp;amp;_foreign_keys=on")&lt;br&gt;
if err != nil {&lt;br&gt;
    return err&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;db.SetMaxOpenConns(1)&lt;br&gt;
db.SetMaxIdleConns(1)&lt;br&gt;
db.SetConnMaxLifetime(0)&lt;br&gt;
db.SetConnMaxIdleTime(0)&lt;br&gt;
That setup reduces write contention and makes the application behave more predictably under load. WAL mode is also important because it lets readers continue while a write transaction is active. Together, WAL and a sensible busy timeout make SQLite much more usable for concurrent forum traffic.&lt;/p&gt;

&lt;p&gt;You should also keep write transactions short. Do not hold a transaction open while doing extra computation, network calls, or template rendering. Start the transaction, write the rows, commit, and release the lock as quickly as possible.&lt;/p&gt;

&lt;p&gt;Preventing lock errors&lt;br&gt;
Lock errors usually happen when the application performs overlapping writes without enough timeout or too many concurrent connections. In SQLite, a writer temporarily needs exclusive access, so a burst of simultaneous post submissions can trigger failures if the database is not configured correctly. The practical fix is a combination of WAL, busy timeout, short transactions, and a low connection count.&lt;/p&gt;

&lt;p&gt;A good operational pattern is:&lt;/p&gt;

&lt;p&gt;Enable WAL mode at startup.&lt;/p&gt;

&lt;p&gt;Enable foreign keys on every connection.&lt;/p&gt;

&lt;p&gt;Set a busy timeout.&lt;/p&gt;

&lt;p&gt;Keep writes small and fast.&lt;/p&gt;

&lt;p&gt;Use a single shared sql.DB.&lt;/p&gt;

&lt;p&gt;Avoid unnecessary parallel writes.&lt;/p&gt;

&lt;p&gt;This is especially relevant in a forum where users may create posts, comments, or likes at the same moment. SQLite can still work well here if the app treats writes carefully rather than assuming the database behaves like a high-concurrency server engine.&lt;/p&gt;

&lt;p&gt;Tagging with many-to-many links&lt;br&gt;
Tags are the best example of a many-to-many relationship in a forum. A post can have multiple tags, and the same tag can belong to many posts. In relational design, this should not be stored as an array column or a comma-separated string. The proper solution is a junction table, which keeps each association as its own row.&lt;/p&gt;

&lt;p&gt;In Go, the user may submit tags as a slice of strings such as:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
tags := []string{"go", "sqlite", "backend"}&lt;br&gt;
That slice is useful at the application boundary, but the database should store normalized records. The usual process is:&lt;/p&gt;

&lt;p&gt;Trim and lowercase each tag.&lt;/p&gt;

&lt;p&gt;Ignore empty values.&lt;/p&gt;

&lt;p&gt;Insert the tag into tags if it does not already exist.&lt;/p&gt;

&lt;p&gt;Insert the post-tag pair into post_tags.&lt;/p&gt;

&lt;p&gt;Use INSERT OR IGNORE or a unique constraint to avoid duplicates.&lt;/p&gt;

&lt;p&gt;A simple implementation might look like this:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
func SavePostTags(tx *sql.Tx, postID int64, tags []string) error {&lt;br&gt;
    for _, raw := range tags {&lt;br&gt;
        name := strings.ToLower(strings.TrimSpace(raw))&lt;br&gt;
        if name == "" {&lt;br&gt;
            continue&lt;br&gt;
        }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    var tagID int64
    err := tx.QueryRow(`SELECT id FROM tags WHERE name = ?`, name).Scan(&amp;amp;tagID)
    if err == sql.ErrNoRows {
        res, err := tx.Exec(`INSERT INTO tags(name, slug) VALUES(?, ?)`, name, name)
        if err != nil {
            return err
        }
        tagID, err = res.LastInsertId()
        if err != nil {
            return err
        }
    } else if err != nil {
        return err
    }

    _, err = tx.Exec(`INSERT OR IGNORE INTO post_tags(post_id, tag_id) VALUES(?, ?)`, postID, tagID)
    if err != nil {
        return err
    }
}
return nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
That method is simple, readable, and close to the database model. It also makes tag queries efficient because the join table is indexed and normalized.&lt;/p&gt;

&lt;p&gt;Querying tags back into Go&lt;br&gt;
When loading a post, you may want the tags as a Go slice again. This is where array manipulation happens on the application side, not inside SQLite. Query the joined rows, scan each row into a Tag, and append it to post.Tags.&lt;/p&gt;

&lt;p&gt;That keeps the storage layer relational and the application layer ergonomic. It also makes it easy to render tags in templates or return them in JSON responses. In practice, this separation is healthier than trying to store “arrays” in the database and unpack them later.&lt;/p&gt;

&lt;p&gt;Putting it together&lt;br&gt;
A good forum backend in Go and SQLite is mostly about respecting the strengths of each tool. SQLite gives you a compact relational store that works well for small and medium workloads, while Go gives you explicit control over struct mapping, transactions, and concurrency. If the schema is normalized and the connection pool is configured carefully, the system stays simple without becoming fragile.&lt;/p&gt;

&lt;p&gt;The final design should aim for these outcomes: clear tables, explicit relationships, safe concurrent access, and a tagging system that scales gracefully. If you follow that structure, you get a forum backend that is easy to maintain, easy to query, and realistic for production use within SQLite’s limits.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>go</category>
    </item>
    <item>
      <title>Writing Clean and Idiomatic Go: A short guide for Developers who Already use Go.</title>
      <dc:creator>Talo Oyweka</dc:creator>
      <pubDate>Mon, 20 Jul 2026 09:23:01 +0000</pubDate>
      <link>https://dev.to/talo_oyweka_d7847a162c1ad/writing-clean-and-idiomatic-go-a-short-guide-for-developers-who-already-use-go-33a9</link>
      <guid>https://dev.to/talo_oyweka_d7847a162c1ad/writing-clean-and-idiomatic-go-a-short-guide-for-developers-who-already-use-go-33a9</guid>
      <description>&lt;ol&gt;
&lt;li&gt;Introduction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Idiomatic Go means writing code that feels natural to Go developers,code that leverages Go’s unique style and conventions rather than code translated from other languages like Java, Python, or C++. Many developers can ship working Go code, but it often retains patterns from other languages, making it less readable and maintainable for the Go community. This article highlights a few high-impact habits that help your Go code move beyond “it works” to “this looks like Go.” By adopting these practices, you’ll write clearer, more maintainable, and idiomatic Go code that aligns with community standards.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Embrace Go’s naming and formatting conventions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go’s formatting and naming conventions are foundational to idiomatic code. Always use gofmt or go fmt and goimports to format your code,style debates end here. Use short, clear variable names, especially for locals (i, n, err, ctx), which are widely accepted. Exported names should use CamelCase, while unexported names use camelCase without underscores.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;go&lt;/p&gt;

&lt;p&gt;// Non-idiomatic&lt;br&gt;
var user_list []User&lt;br&gt;
func Get_user_list() []User { ... }&lt;/p&gt;

&lt;p&gt;// Idiomatic&lt;br&gt;
var users []User&lt;br&gt;
func ListUsers() []User { ... }&lt;br&gt;
Following these conventions makes your code instantly more readable and approachable to other Go developers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prefer simple, flat control flow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go favors straightforward, flat control flow over deep nesting or clever tricks. Use early returns to avoid nested if statements and unnecessary else blocks.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;go&lt;/p&gt;

&lt;p&gt;// Less idiomatic&lt;br&gt;
func Process(u *User) error {&lt;br&gt;
    if u != nil {&lt;br&gt;
        if u.Active {&lt;br&gt;
            // ...&lt;br&gt;
            return nil&lt;br&gt;
        } else {&lt;br&gt;
            return errors.New("user not active")&lt;br&gt;
        }&lt;br&gt;
    } else {&lt;br&gt;
        return errors.New("user is nil")&lt;br&gt;
    }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// More idiomatic&lt;br&gt;
func Process(u *User) error {&lt;br&gt;
    if u == nil {&lt;br&gt;
        return errors.New("user is nil")&lt;br&gt;
    }&lt;br&gt;
    if !u.Active {&lt;br&gt;
        return errors.New("user not active")&lt;br&gt;
    }&lt;br&gt;
    // ...&lt;br&gt;
    return nil&lt;br&gt;
}&lt;br&gt;
This approach keeps code easy to read and maintain without sacrificing clarity.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Idiomatic error handling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go uses explicit error values instead of exceptions. Always check errors immediately after the function call and handle or return them promptly. Use fmt.Errorf with %w to wrap errors and add context.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;go&lt;/p&gt;

&lt;p&gt;f, err := os.Open(path)&lt;br&gt;
if err != nil {&lt;br&gt;
    return fmt.Errorf("open %s: %w", path, err)&lt;br&gt;
}&lt;br&gt;
defer f.Close()&lt;br&gt;
Avoid ignoring errors (e.g., _ = fn()) and long chains of if err != nil by extracting helper functions when appropriate._&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use interfaces for behavior, not for everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Avoid defining interfaces “just in case.” Instead, create small, focused interfaces where they are consumed, describing only the behavior needed. Prefer concrete types until abstraction is necessary for testing or flexibility.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;go&lt;/p&gt;

&lt;p&gt;// Less idiomatic: interface for everything&lt;br&gt;
type UserRepository interface {&lt;br&gt;
    Save(u User) error&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;type UserService struct {&lt;br&gt;
    repo UserRepository&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// More idiomatic: define interface where needed&lt;br&gt;
type Saver interface {&lt;br&gt;
    Save(User) error&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;func RegisterUser(s Saver, u User) error {&lt;br&gt;
    // ...&lt;br&gt;
    return s.Save(u)&lt;br&gt;
}&lt;br&gt;
Small, focused interfaces are more idiomatic than large object-oriented hierarchies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Organize packages by domain, not by layer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Structure your project by domain concepts rather than technical layers. Use package names that reflect their responsibility (e.g., user, billing, store) instead of generic names like utils or helpers. Keep package APIs minimal and export only what’s necessary.&lt;/p&gt;

&lt;p&gt;Example structure:&lt;/p&gt;

&lt;p&gt;/cmd/app/main.go&lt;br&gt;
/internal/user/      // user logic&lt;br&gt;
/internal/store/     // data access&lt;br&gt;
/internal/httpapi/   // HTTP handlers&lt;br&gt;
Idiomatic Go projects are simple but clearly divided by domain responsibilities.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Testing as part of idiomatic style&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Testing is integral to idiomatic Go. Use Go’s built-in testing framework with _test.go files and the testing package. Table-driven tests are common and encouraged. Keep tests in the same package to access both exported and unexported code.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;go&lt;/p&gt;

&lt;p&gt;func Add(a, b int) int { return a + b }&lt;/p&gt;

&lt;p&gt;// add_test.go&lt;br&gt;
func TestAdd(t *testing.T) {&lt;br&gt;
    tests := []struct {&lt;br&gt;
        name     string&lt;br&gt;
        a, b, want int&lt;br&gt;
    }{&lt;br&gt;
        {"simple", 1, 2, 3},&lt;br&gt;
        {"zero", 0, 0, 0},&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        if got := Add(tt.a, tt.b); got != tt.want {
            t.Fatalf("Add(%d,%d)=%d; want %d", tt.a, tt.b, got, tt.want)
        }
    })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
Tests help keep code clean and enable safe refactoring.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Conclusion (60–100 words)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Idiomatic Go emphasizes clarity, simplicity, and consistency. By embracing Go’s formatting, naming, control flow, error handling, interfaces, package organization, and testing conventions, your code becomes more maintainable and idiomatic. Always run gofmt and study the standard library and popular packages like net/http and context for style guidance. For further learning, consult resources like Effective Go and Go Code Review Comments by the Go team.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>go</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Navigating Intellectual Property as a software Developer.</title>
      <dc:creator>Talo Oyweka</dc:creator>
      <pubDate>Thu, 16 Jul 2026 09:59:03 +0000</pubDate>
      <link>https://dev.to/talo_oyweka_d7847a162c1ad/navigating-intellectual-property-as-a-software-developer-5e73</link>
      <guid>https://dev.to/talo_oyweka_d7847a162c1ad/navigating-intellectual-property-as-a-software-developer-5e73</guid>
      <description>&lt;p&gt;What I learned at Zone01 Kisumu's IP training&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
Yesterday, I attended an Intellectual Property training at Zone01 Kisumu, and it fundamentally changed how I think about the code I write. As developers, we spend countless hours building software, but how many of us truly understand the value of what we create,and how to protect it?&lt;/p&gt;

&lt;p&gt;In Kenya's rapidly growing tech ecosystem, understanding IP isn't just a legal nicety, it's a competitive advantage. Whether you're building an app, contributing to open source, or launching a startup, knowing your rights can mean the difference between owning your work and losing it.&lt;/p&gt;

&lt;p&gt;This article breaks down the essential IP frameworks every software developer should know.&lt;/p&gt;

&lt;p&gt;What is Intellectual Property in Software?&lt;br&gt;
Intellectual Property (IP) in software encompasses legal protections designed to safeguard the rights of creators and developers. The four primary types of IP protection relevant to software are: patents, copyrights, trademarks, and trade secrets .&lt;/p&gt;

&lt;p&gt;Copyright: Protecting Your Code&lt;br&gt;
Copyright is the most immediate form of protection for software developers. In Kenya, copyright protection arises automatically at the moment of creation,registration is not mandatory . This means that when you write code, you automatically own the copyright to that expression, provided the work is fixed in a tangible medium .&lt;/p&gt;

&lt;p&gt;However, a crucial distinction exists: copyright protects the expression of ideas, not the ideas themselves . This principle was reinforced in the Kenyan case of Solut Technology Limited v Safaricom Limited, where the court confirmed that without access to source code, it's difficult to prove infringement because the "expression" (the code itself) wasn't shared .&lt;/p&gt;

&lt;p&gt;Key Insight: Registering your copyright with KECOBO in Kenya provides prima facie evidence of ownership and can make enforcement faster if someone copies your work .&lt;/p&gt;

&lt;p&gt;Patents: Protecting Functionality&lt;br&gt;
While copyright protects the expression of code, patents protect novel and non-obvious functional inventions . Software patents cover innovative algorithms, technical solutions, and processes implemented by software. A well-crafted patent can provide a significant competitive advantage, preventing others from using similar methods .&lt;/p&gt;

&lt;p&gt;However, obtaining a patent is a rigorous process requiring novelty, creativity, and practical applicability . The patent application process for software inventions requires careful consideration of eligibility criteria and thorough research to avoid challenges from patent trolls .&lt;/p&gt;

&lt;p&gt;Trade Secrets: Protecting Confidential Information&lt;br&gt;
Trade secrets encompass confidential business information that provides competitive advantage, including source code, algorithms, and strategic data . Unlike copyrights or patents, trade secrets rely on maintaining secrecy rather than formal registration .&lt;/p&gt;

&lt;p&gt;For software developers, trade secret protection requires:&lt;/p&gt;

&lt;p&gt;Implementing strict confidentiality agreements&lt;/p&gt;

&lt;p&gt;Limiting access to sensitive information&lt;/p&gt;

&lt;p&gt;Establishing clear protocols for handling proprietary data &lt;/p&gt;

&lt;p&gt;Trademarks: Protecting Your Brand&lt;br&gt;
Trademarks protect the brand identity of your software product,names, logos, and distinctive symbols that identify your goods or services . Registering trademarks with KIPI in Kenya protects your brand from copycats and strengthens your IP portfolio .&lt;/p&gt;

&lt;p&gt;Joint IP Rights in Collaborative Development&lt;br&gt;
When multiple parties collaborate on software development, joint intellectual property rights can arise. Joint ownership occurs when multiple parties contribute inseparably or interdependently to integrated software components .&lt;/p&gt;

&lt;p&gt;Key considerations for joint IP ownership include:&lt;/p&gt;

&lt;p&gt;Factor  Description&lt;br&gt;
Contribution Scope  Degree to which each party's work is crucial&lt;br&gt;
Integration Complexity  Level of interdependence among components&lt;br&gt;
Contractual Agreements  Terms defining rights and responsibilities&lt;br&gt;
Innovation Interdependence  How innovations rely on joint efforts &lt;br&gt;
Joint ownership confers co-owners the ability to exploit the intellectual property, subject to agreed terms or statutory provisions. However, without clear agreements, conflicts may arise regarding sublicensing or commercialization .&lt;/p&gt;

&lt;p&gt;Open Source Software and Licensing&lt;br&gt;
Open source software presents a unique intersection with IP law. An open source license is an IP license and legal agreement that grants users certain rights to use, inspect, distribute, and modify software .&lt;/p&gt;

&lt;p&gt;Types of Open Source Licenses&lt;br&gt;
Copyleft/Reciprocal Licenses (e.g., GPL) require users to release any modifications made to the software under the same license. These licenses protect against proprietary software development and ensure that contributions remain freely available .&lt;/p&gt;

&lt;p&gt;Permissive Licenses (e.g., MIT, Apache, BSD) do not impose restrictions on derivative works, allowing companies to incorporate code into proprietary projects without releasing their own code under the same license .&lt;/p&gt;

&lt;p&gt;Key Considerations for Open Source&lt;br&gt;
Copyright vs. Open Source: Traditional copyright restricts use without permission, while open source licenses outline permitted uses .&lt;/p&gt;

&lt;p&gt;Patent Grants: Some licenses (Apache v2.0, GPL 3.0) include explicit patent grants, while others (MIT, BSD) do not .&lt;/p&gt;

&lt;p&gt;Compliance Risks: Incorporating open source without understanding license obligations can create legal risks .&lt;/p&gt;

&lt;p&gt;Protecting Your IP When Working with Developers&lt;br&gt;
This is particularly relevant for founders and entrepreneurs who aren't coders themselves.&lt;/p&gt;

&lt;p&gt;The Kenyan Legal Framework&lt;br&gt;
In Kenya, copyright law treats the person who creates the code as the owner by default . Paying for development does not by itself give you ownership of the code. The most common mistake is to assume that payment equals ownership.&lt;/p&gt;

&lt;p&gt;Essential Contracts&lt;br&gt;
Non-Disclosure Agreement (NDA): Before sharing your idea, have the developer sign an NDA that defines what is confidential and how the information must be handled .&lt;/p&gt;

&lt;p&gt;Written Development Agreement: The agreement should clearly specify that:&lt;/p&gt;

&lt;p&gt;You own all rights in the app once you've paid&lt;/p&gt;

&lt;p&gt;The developer is doing work for hire&lt;/p&gt;

&lt;p&gt;All IP rights are assigned to you on completion&lt;/p&gt;

&lt;p&gt;The work is original &lt;/p&gt;

&lt;p&gt;Handling Third-Party Components: Your agreement should address third-party software licenses and open source components .&lt;/p&gt;

&lt;p&gt;Common Risks to Address&lt;br&gt;
Developers withholding source code until extra fees are paid&lt;/p&gt;

&lt;p&gt;Developers keeping passwords and hosting access&lt;/p&gt;

&lt;p&gt;Code being reused in another client's project&lt;/p&gt;

&lt;p&gt;Missing documentation so no one else can maintain the app &lt;/p&gt;

&lt;p&gt;Protecting Your App Idea in Kenya&lt;br&gt;
For entrepreneurs in Kenya's tech space, there are specific local considerations:&lt;/p&gt;

&lt;p&gt;Understanding the Law&lt;br&gt;
Ideas aren't protected , copyright protects the expression of the idea (the code, the design, the brand) &lt;/p&gt;

&lt;p&gt;Copyright is automatic , but registration with KECOBO provides an official record of ownership &lt;/p&gt;

&lt;p&gt;Trademarks , register with KIPI for brand protection &lt;/p&gt;

&lt;p&gt;Practical Steps&lt;br&gt;
Keep records of your development (notes, sketches, prototypes, email trails) to prove ownership &lt;/p&gt;

&lt;p&gt;Build privacy and security into the design from the start &lt;/p&gt;

&lt;p&gt;Include data protection obligations in your contracts &lt;/p&gt;

&lt;p&gt;Generative AI and IP Risks&lt;br&gt;
The use of generative AI tools (like GitHub Copilot) presents new IP challenges:&lt;/p&gt;

&lt;p&gt;Ownership Uncertainty: There's more uncertainty around ownership of computer-generated works than for works created by a human .&lt;/p&gt;

&lt;p&gt;Copyright Infringement Risk: Code suggested by AI tools may amount to a substantial copy of third-party code on which the AI model was trained. This is particularly concerning for open source license compliance .&lt;/p&gt;

&lt;p&gt;Trade Secret Exposure: Using consumer AI tools can expose proprietary information and code .&lt;/p&gt;

&lt;p&gt;Best Practices&lt;br&gt;
Have a generative AI policy specifying which tools are permitted and how they can be used&lt;/p&gt;

&lt;p&gt;Check terms and conditions of AI tools thoroughly to ensure your organization owns outputs&lt;/p&gt;

&lt;p&gt;Consider scanning software to audit it for open source code &lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
As the Solut Technology Limited v Safaricom Limited case demonstrated, not understanding IP law can be costly. The plaintiff's claim failed primarily because they couldn't prove ownership of a protected copyright .&lt;/p&gt;

&lt;p&gt;Whether you're a developer, founder, or both, understanding IP is essential for protecting the value you create. The frameworks outlined here provide a foundation—but the most important step is to seek professional legal advice tailored to your specific situation.&lt;/p&gt;

&lt;p&gt;Key Takeaways:&lt;/p&gt;

&lt;p&gt;Copyright protects your code's expression; patents protect its functionality&lt;/p&gt;

&lt;p&gt;Register your IP for stronger enforcement rights&lt;/p&gt;

&lt;p&gt;When working with others, get everything in writing—including ownership terms&lt;/p&gt;

&lt;p&gt;Understand the implications of open source licenses&lt;/p&gt;

&lt;p&gt;Keep records to prove ownership if disputes arise&lt;/p&gt;

&lt;p&gt;What's your experience with IP as a developer? I'd love to hear your thoughts in the comments.&lt;/p&gt;

</description>
      <category>career</category>
      <category>learning</category>
      <category>opensource</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Beyond the Silicon Savannah: Navigating the AI Upskilling Divide in Kisumu, Kenya.</title>
      <dc:creator>Talo Oyweka</dc:creator>
      <pubDate>Tue, 26 May 2026 12:34:32 +0000</pubDate>
      <link>https://dev.to/talo_oyweka_d7847a162c1ad/beyond-the-silicon-savannah-navigating-the-ai-upskilling-divide-in-kisumu-kenya-99f</link>
      <guid>https://dev.to/talo_oyweka_d7847a162c1ad/beyond-the-silicon-savannah-navigating-the-ai-upskilling-divide-in-kisumu-kenya-99f</guid>
      <description>&lt;p&gt;The global discourse surrounding artificial intelligence (AI) in emerging economies frequently centers on capital cities. In Kenya, Nairobi’s vibrant "Silicon Savannah" captures the lion’s share of venture capital, tech infrastructure, and policy focus. However, the true litmus test for digital inclusion across Sub-Saharan Africa lies not in its primary metropolises, but in its rapidly growing intermediary cities.&lt;/p&gt;

&lt;p&gt;Kisumu, Kenya’s third-largest city and a lakeside economic hub, serves as a critical case study for this transition. As AI tools reshape global value chains, the youth demographic in secondary urban centers stands at a pivotal crossroads. While the expansion of the digital economy offers unprecedented pathways for economic mobility, a stark divide is emerging between basic tool access and genuine AI literacy.&lt;/p&gt;

&lt;p&gt;The Fluency Gap: Access vs. Competence&lt;/p&gt;

&lt;p&gt;A common misconception among policymakers is that widespread smartphone penetration and mobile internet access organically translate into digital literacy. In Kisumu’s informal settlements like Manyatta or Nyalenda, young people frequently interact with consumer-facing AI through social media algorithms and machine-translated content. Yet, this represents passive consumption rather than active technological agency.&lt;/p&gt;

&lt;p&gt;A profound skills gap persists between navigating an AI interface and possessing true technical fluency. True AI literacy entails understanding data pipeline engineering, foundational prompt architecture, and model training concepts. In the local gig economy, where many youths rely on digital platforms for freelance tasks, the rise of automation is already inducing a high rate of job churn.&lt;/p&gt;

&lt;p&gt;Without specialized skills, local workers are restricted to low-tier, repetitive micro-tasks,such as basic data entry,which are the most vulnerable to complete automation. The challenge for local educational ecosystems is to transition youth from being mere operators of digital platforms to becoming architects and auditors of AI services.&lt;/p&gt;

&lt;p&gt;Infrastructure and the Realities on the Ground&lt;/p&gt;

&lt;p&gt;Intermediary cities encounter structural bottlenecks that are less pronounced in primary tech hubs. In Kisumu, the implementation of comprehensive AI upskilling faces two foundational challenges: inconsistent electrical grid infrastructure and the high cost of reliable, high-bandwidth broadband internet.&lt;/p&gt;

&lt;p&gt;AI development and advanced model training require substantial computing power. While cloud-based environments mitigate the need for high-end local hardware, they remain entirely dependent on robust, uninterrupted internet connectivity. For a student at a local technical and vocational education and training (TVET) institution, a temporary power outage or a fluctuating mobile data connection can disrupt advanced coding or machine learning simulations.&lt;/p&gt;

&lt;p&gt;Mapping the Local Ecosystem: Initiatives and Innovations&lt;/p&gt;

&lt;p&gt;To address these challenges, a decentralized patchwork of initiatives has emerged involving county governments, NGOs, and private sector entities.&lt;/p&gt;

&lt;p&gt;Local Digital Transformation Map&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   [National Digital Masterplan] 
                 │
     ┌───────────┴───────────┐
     ▼                       ▼
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;[County Digital Hubs]  &lt;a href="https://dev.toBasic%20Access%20&amp;amp;%20Labs"&gt;Private/NGO Labs&lt;/a&gt;  (Specialized Training)&lt;br&gt;
         │                       │&lt;br&gt;
         └───────────┬───────────┘&lt;br&gt;
                     ▼&lt;br&gt;
          &lt;a href="https://dev.toPeer%20Mentorship%20&amp;amp;%20AI%20Literacy"&gt;Local Youth Cohorts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A concrete local example is the operationalization of localized innovation spaces across Kisumu County. Supported by the national digital masterplan, these hubs aim to provide free internet access and workstation facilities. Concurrently, local grassroots technology cooperatives are offering specialized modules on data annotation and localized AI application design.&lt;/p&gt;

&lt;p&gt;Economic Pathways and the Threat of Job Churn&lt;br&gt;
Kenya has historically positioned itself as a regional anchor for business process outsourcing (BPO). However, econometric projections suggest that AI automation could disrupt over half of traditional low-skill service jobs within the country.&lt;/p&gt;

&lt;p&gt;For youth in Kisumu, this presents a fork in the road:&lt;/p&gt;

&lt;p&gt;The Risk: If the local labor force remains constrained to basic digital literacy, automation could displace a large share of the region’s online freelancers.&lt;br&gt;
The Opportunity: If upskilling programs successfully cultivate competencies in model validation and localized dataset creation, Kisumu can compete in high-value digital service exports due to its lower cost of operations compared to Nairobi.&lt;br&gt;
Mitigating the Risk of Secondary Digital Exclusion&lt;br&gt;
To prevent a multi-tiered domestic digital divide, local development frameworks must implement structural changes:&lt;/p&gt;

&lt;p&gt;Curriculum Integration: TVET centers must look beyond desktop publishing and integrate data ethics and algorithmic reasoning into standard curricula.&lt;br&gt;
Targeted Inclusion Frameworks: Strategic design is required to ensure equitable access across gender boundaries, including flexible learning schedules.&lt;br&gt;
Public-Private Infrastructure Partnerships: County administrations can establish infrastructure sharing agreements to deliver subsidized data bundles dedicated to accredited educational platforms.&lt;/p&gt;

&lt;p&gt;Conclusion: A Strategic Path Forward&lt;br&gt;
The future of AI upskilling in intermediary cities like Kisumu depends on moving past generic digital literacy programs in favor of targeted technical training. Access to devices is a necessary baseline, but sustainable economic inclusion requires practical fluency in handling data and managing AI models.&lt;/p&gt;

&lt;p&gt;Let's Discuss 💬&lt;br&gt;
The transition from "Silicon Savannah" to a country-wide tech ecosystem is complex. I'd love to hear your thoughts:&lt;/p&gt;

&lt;p&gt;I'll be in the comments to discuss,let's share insights!&lt;/p&gt;

&lt;p&gt;Details References&lt;/p&gt;

&lt;p&gt;Graduate Institute of International and Development Studies. (2025). Trade and labour: Pathways for decent work in Kenya's digital economy. Centre for Regions, Trade and Geopolitics; Thinking Ahead on Societal Change (TASC) Platform.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>discuss</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
