Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
151752 stories
·
33 followers

The Secret Life of Go: Error Handling

1 Share

Chapter 12: The Sound of Breaking Glass

Monday morning arrived with a heavy gray mist clinging to the city. Inside the archive, the silence was absolute, broken only by the rhythmic scrape-scrape-scrape of Eleanor’s bone folder smoothing a crease in an ancient map.

Ethan walked in, his umbrella dripping. He set a small bakery box on the desk. "Pear and almond tart. And a Flat White, extra foam."

Eleanor paused her work. She inspected the tart. "A classic combination. The sweetness of the pear balances the bitterness of the almond. Well chosen."

Ethan sat down, opening his laptop with a sigh that was a little too loud.

"That sound," Eleanor said without looking up. "That is the sigh of a programmer fighting the compiler."

"Not the compiler," Ethan corrected. "The boilerplate. I'm writing this file parser, and half my code is just checking for errors. It feels… primitive. I miss exceptions. I miss try-catch blocks where I can just wrap everything in a bubble and handle problems at the end."

He turned his screen to her.

func ParseFile(filename string) {
    f, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    // ... logic continues
}

"I was thinking," Ethan said. "Maybe I should just panic when things go wrong and recover at the top level? Like a global exception handler?"

Eleanor set down her bone folder. Her expression hardened.

"Ethan, imagine you are reading a book," she began. "You are on page 50. Suddenly, without warning, you are teleported to page 200. You have no idea how you got there or what happened in between. That is an Exception."

She took a sip of the Flat White. "Exceptions are invisible control flow. They allow a function to jump the stack, bypassing return statements and cleanup logic. In Go, we value visibility over convenience. We treat errors as Values."

Errors are just Values

She opened a new file. "An error in Go is not a magical event. It is a value, just like an integer or a string. It is a simple interface:"

type error interface {
    Error() string
}

"It is just anything that can describe itself as a string. Because it is a value, you can pass it, store it, wrap it, or ignore it—though you certainly shouldn't."

She typed a new example. "You said you wanted to panic. Let me show you why treating errors as values is better."

package main

import (
    "errors"
    "fmt"
    "os"
)

// Define a "Sentinel Error" - a constant value we can check against
var ErrEmptyConfig = errors.New("config file is empty")

func loadConfig(path string) (string, error) {
    if path == "" {
        // We create the error value right here
        return "", errors.New("path cannot be empty")
    }

    file, err := os.Open(path)
    if err != nil {
        // We return the error value up the stack
        return "", err
    }
    defer file.Close()

    stat, err := file.Stat()
    if err != nil {
        return "", err
    }

    if stat.Size() == 0 {
        // We return our specific sentinel error
        return "", ErrEmptyConfig
    }

    return "config_data", nil
}

func main() {
    _, err := loadConfig("missing.txt")
    if err != nil {
        fmt.Println("Error occurred:", err)
    }
}

"Look at the flow," Eleanor pointed. "There are no hidden jumps. The error travels up the stack through return values. You can see exactly where it exits. This is Explicit Control Flow."

"But it's so verbose," Ethan grumbled. "if err != nil is everywhere."

"It is repetitive," Eleanor conceded. "But consider the alternative. If file.Stat() threw an exception, would you remember to close the file? In Go, the defer file.Close() runs no matter what errors happen later. The explicit checks force you to decide: 'What do I do if this fails right now?'"

Decorating the Error

"However," Eleanor added, "simply returning err is often lazy. If loadConfig returns 'file not found', the caller doesn't know which file. You need to add context."

She modified the code:

func loadConfig(path string) (string, error) {
    file, err := os.Open(path)
    if err != nil {
        // Wrap the error with context using %w
        // We only wrap when we are adding useful information.
        return "", fmt.Errorf("failed to open config at %s: %w", path, err)
    }
    // ...
}

"The %w verb stands for 'wrap'. It puts the original error inside a new one. It creates a chain."

Ethan frowned. "But if I wrap it, how do I check what the original error was? If I want to check for ErrEmptyConfig?"

"A brilliant question." Eleanor opened her notebook. "Before Go 1.13, this was hard. Now, we have errors.Is and errors.As."

Unwrapping the Mystery

She typed a robust error handling example:

package main

import (
    "errors"
    "fmt"
    "io/fs"
)

func main() {
    _, err := loadConfig("config.json")
    if err != nil {
        // 1. Check if it matches a specific value (Sentinel)
        if errors.Is(err, ErrEmptyConfig) {
            fmt.Println("Please provide a non-empty config file.")
            return
        }

        // 2. Check if it matches a specific TYPE (like PathError)
        var pathErr *fs.PathError
        if errors.As(err, &pathErr) {
            fmt.Println("File system error on path:", pathErr.Path)
            return
        }

        // 3. Fallback
        fmt.Println("Unknown error:", err)
    }
}

"Think of errors.Is like checking equality. It looks through the layers of wrapping to see if ErrEmptyConfig is buried somewhere inside. Think of errors.As like a type assertion. It checks if there is an error of type *fs.PathError inside."

Ethan studied the code. "So I can wrap an error ten times, adding context at every layer, and errors.Is can still find the original cause?"

"Exactly. You preserve the root cause while adding the narrative of how it failed."

Don't Panic

"So when can I use panic?" Ethan asked.

"Almost never," Eleanor replied sternly. "Panic is for when the program is fundamentally broken and cannot continue—like running out of memory, or a developer mistake that makes the internal state invalid. It is not for 'file not found' or 'network timeout'."

She picked up the tart. "If you panic in a library I import, you crash my entire application. That is rude. Return an error. Let me decide if I want to crash."

Ethan watched the rain streak against the window. "It forces you to handle the unhappy path first."

"Yes. It aligns your code to the left," Eleanor said, using her hand to trace the shape of the code in the air. "The 'happy path'—the successful logic—stays minimally indented on the left side of the screen. The error handling nests to the right and returns early. It makes the logic easy to scan."

Eleanor took a bite of the tart. "An error is not an exception, Ethan. An exception says 'something unexpected happened.' An error says 'this is a possible outcome of this function.' And in software, failure is always a possible outcome."

She wiped a crumb from her lip. "Treat your errors with respect. Give them context. Check them specifically. And never, ever assume they won't happen."

Ethan closed his laptop lid. "No more try-catch."

"No more try-catch," Eleanor agreed. "Just values. Passed from hand to hand, until they are resolved."

Key Concepts from Chapter 12

Errors are Values: In Go, errors are not special control flow mechanisms. They are values implementing the error interface.

The error Interface:

type error interface {
    Error() string
}

Sentinel Errors: Pre-defined global variables for specific errors (e.g., io.EOF, errors.New("some error")). Useful for simple equality checks.

Wrapping Errors: Using fmt.Errorf("... %w ...", err) wraps an error, adding context while preserving the original error underneath.

errors.Is: Checks if a specific error value exists anywhere in the wrapping chain. Use this instead of == when handling wrapped errors.

  • Example: if errors.Is(err, io.EOF)

errors.As: Checks if an error of a specific type exists anywhere in the chain, and assigns it to a variable.

var pathErr *fs.PathError // Note: must be a pointer to the error type
if errors.As(err, &pathErr) {
    fmt.Println(pathErr.Path)
}

Panic vs. Error:

  • Error: Expected failure modes (I/O, validation, network). Handled by returning values.
  • Panic: Unexpected, unrecoverable state (nil pointer, index out of range). Crashes the program (unless recovered, which is rare).
  • Rule: Don't panic in libraries. Return errors.

Align the Happy Path: Structure code to handle errors early and return. Keep the successful logic minimally indented.

Next chapter: Testing—where Eleanor shows Ethan that writing tests isn't a chore, but the only way to prove his system actually works.

Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.

Read the whole story
alvinashcraft
34 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

💡 How to Install Gemini CLI on Your Computer

1 Share

Gemini CLI is a great open source command line tool that lets you use Google's Gemini on your computer right in the terminal. You can install and use it on Windows, Mac, or Linux. It can help you with code development, file and project management, and AI-powered assistance. Why I like and use it is also not only because it is a very powerful tool, but also because of its cost - or in reality - no cost! For individuals, it has a 1000 requests per day limit, which is way too much for the average user (60 requests per minute). So, try it, see if you like it, and comment under the article. Now, let's install it:

🛠 Prerequisites

  • Node.js (v20 or higher) — this provides the runtime environment Gemini CLI needs.
  • A working terminal.

🚀 Step 1 — Install Gemini CLI

npm install -g @google/gemini-cli

▶️ Step 2 — Launch Gemini CLI

In your command-line shell type:

gemini

🔑 Step 3 — Authenticate

You will need to log in with your Google account - that is how Google will track how many requests you make. But don't worry, they are giving more than needed for an average user.

🧪 Step 4 — Try a Command!

Enter a prompt and wait for an answer. My question about why the sky is blue—it took 9 seconds to answer :)

About the Author

Deividas Strole is a Full-Stack Developer based in California, specializing in Java, Spring Boot, React, and AI-driven development. He writes about software engineering, modern full-stack development, and digital marketing strategies.

Connect with me:

Read the whole story
alvinashcraft
35 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Control Your Space or You Lose Your Mind Working Remote

1 Share

Remote work is sold as freedom. Work from anywhere. Laptop by the pool. Coffee shop vibes. Total flexibility.

That story sounds good, but it is mostly nonsense.

Ashkan Rajaee argues something uncomfortable that most remote founders do not want to hear. If you do not aggressively control your environment, remote work will eventually break you and stall your business.

This idea goes against popular remote culture, and that is exactly why it matters.

The Hidden Ceiling Nobody Talks About

Most people think remote failure comes from lack of discipline, motivation, or talent. That assumption is flawed.

The real bottleneck is environment.

According to :contentReference[oaicite:0]{index=0}, every remote operator eventually hits a ceiling if their workspace is chaotic, shared, or constantly shifting. You might survive short term. You might even grow a bit. But at scale, cracks turn into collapse.

The logic is simple. If you do not control your space, something else does. Family. Noise. Bad internet. Missing gear. All of it drains cognitive energy that should be spent building.

This is not about comfort. It is about leverage.

One Space. Total Control.

Rajaee’s core rule is brutal and clear. You need one place that you fully control.

Not five locations. Not a backpack setup. One environment where noise, internet, and equipment are predictable every single day.

That might sound extreme, but consider the alternative. Constant setup and teardown. Forgotten chargers. Spotty WiFi. Interrupted calls. Each issue feels small, but over a year, the damage compounds.

You cannot scale a serious operation while mentally tracking cables and battery percentages.

Noise Is the First Enemy

Noise is not just sound. It is interruption.

If anyone can walk into your workspace while you are working, you are not in control. Period.

This includes well meaning partners, kids, roommates, or anyone else sharing your space. Even quick questions fracture focus. Deep work does not survive open doors.

Rajaee recommends a dedicated office, even a small one person room. A Regus office near home works. So does a properly isolated home office. What matters is the boundary.

No access. No interruptions. No negotiation.

This is not antisocial. It is professional.

Internet Is Not the Place to Save Money

Residential internet is cheaper. It is also unreliable.

Commercial internet costs more for a reason. Service level agreements. Dedicated support. No throttling. Faster fixes when something breaks.

Dropped calls are not just annoying. They erode trust with clients, partners, and teams. Multiply those drops across hundreds of calls per year and the cost becomes obvious.

If your income depends on being online, your internet is infrastructure, not a utility.

Gear Multiplies Output or Destroys It

Minimalism is trendy. It is also inefficient at scale.

Rajaee is known for running extreme multi monitor setups, sometimes up to seven screens. That might sound excessive, but the reasoning is solid. Every window switch burns mental energy.

Gestures. Swipes. Tab juggling. Those are micro costs that add up fast.

Multiple monitors externalize information. Calendars stay visible. Dashboards stay open. Decisions get faster.

The same applies to peripherals. Dedicated microphones. Cameras. Lighting. Backup devices. Redundancy is not luxury. It is insurance against friction.

Why Laptop Nomad Culture Fails

Working from anywhere sounds empowering until you track the hidden costs.

Every pack and unpack shifts focus away from strategy and execution. Instead of thinking about growth, you think about adapters, batteries, and cables.

Rajaee calls this the biggest mistake remote workers make. Mobility becomes a distraction disguised as freedom.

If you must travel, fine. But your core workspace should always be waiting for you, powered on, configured, and ready.

No friction. No setup rituals. No excuses.

Why Companies Force Offices

Here is the uncomfortable truth.

Companies want employees in offices because offices work.

Not because of control for its own sake, but because structured environments eliminate chaos. Internet is there. Gear is there. The workspace is ready every day.

That structure produces efficiency. Efficiency produces profit.

Remote founders who ignore this lesson try to run companies without the very systems businesses have relied on for decades.

The Non Negotiable Rule

If you want to build a serious company remotely, control your space or accept the ceiling.

Noise. Internet. Gear.

Ignore any one of them and growth becomes fragile.

Remote work is not about being anywhere. It is about being effective.

And effectiveness starts with an environment that serves the work instead of sabotaging it.

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

React Native 0.83 for Production Teams: Better DevTools, Better Tracing, Less Risk

1 Share

🚀 React Native 0.83: stability-first + a bundled native DevTools desktop app

React Native 0.83 is a stability-focused release that ships React 19.2, meaningful upgrades to React Native DevTools, and support for Web Performance APIs (now stable) plus IntersectionObserver (Canary). It’s also the first React Native release with no user-facing breaking changes, which makes it especially appealing for teams maintaining production apps. This post walks through what’s new, how the DevTools workflow changes, and how to approach the 0.82 → 0.83 upgrade with minimal risk.

🧭 Context: why 0.83 matters for production teams

React Native 0.83 is positioned as a more predictable upgrade: if you’re on 0.82, you should be able to move to 0.83 without changes to your app code. That matters for production teams because it lowers upgrade overhead and lets you invest time in validation (performance, crash rates, networking behavior) rather than reactive refactors. On top of that, DevTools improvements (Network + Performance panels and a new desktop app) directly shorten debugging cycles for large apps.

🆕 What’s new in React Native 0.83

⚛️ React 19.2 (including <Activity> and useEffectEvent)

React Native 0.83 includes React 19.2 and brings the new <Activity> and useEffectEvent APIs to React Native. The release notes also mention a critical security vulnerability in React Server Components and stress that React Native is not directly affected, because it doesn’t depend on the impacted packages (react-server-dom-*), while warning monorepo users to audit and upgrade those packages if present. The post also states that React dependencies will be updated to 19.2.1 in the next patch release.

🧩 <Activity>: prioritize UI subtrees while preserving state

<Activity> lets you split an app into “activities” that can be controlled and prioritized, as an alternative to conditional rendering. It supports two modes: visible (shows children, mounts effects, processes updates normally) and hidden (hides children, unmounts effects, and defers updates until React has nothing left to work on). A key behavior is that trees hidden with <Activity mode="hidden"> preserve their state, so when they become visible again they can keep things like search status and a previous selection.

🧠 useEffectEvent: separate “event” logic from reactive effects

The release notes describe a common useEffect pattern: notifying app code about an “event” from an external system, which can unintentionally cause the effect to re-run whenever any value used inside that event changes. They also note that many developers work around this by disabling the lint rule and excluding dependencies—at the cost of potential bugs later. With useEffectEvent, you can split the “event” part out of the effect that emits it, keeping effects more correct and maintainable.

🛠️ New DevTools features (Network + Performance)

React Native 0.83 delivers “long awaited features and quality of life improvements” to React Native DevTools. Two major additions are Network inspection and Performance tracing, both available now.

  • Network inspection shows network requests with metadata such as timings and headers, includes response previews, and adds an Initiator tab to see where in code a request originated.
  • Today, network coverage includes calls made via fetch(), XMLHttpRequest, and <Image>, with support for custom networking libraries (like Expo Fetch) planned for later.
  • For Expo apps, the notes explain you’ll still see the separate “Expo Network” panel (with broader event coverage, but no request initiator, and no Performance panel integration).
  • Performance tracing records a session and shows JavaScript execution, React Performance tracks, network events, and custom User Timings in a single timeline.

The post explicitly connects this to the Web Performance APIs support in 0.83 and encourages teams to incorporate the Performance panel into daily workflow to better understand what makes apps slow.

🖥️ DevTools goes desktop: bundled native app

Previously, React Native DevTools launched in a browser window and required Chrome or Edge to be installed. In 0.83, React Native introduces a new bundled desktop app with the same “zero-install setup,” but no web browser requirement, faster launch via a lightweight notarized desktop binary, and better windowing behavior (including macOS multitasking improvements, auto-raise on breakpoint, and restoring window arrangements). The release notes also say reliability improves because DevTools runs separately from a personal browser profile, avoiding issues caused by certain preinstalled Chrome extensions, and that in rare cases where the desktop binary can’t be downloaded (e.g., corporate firewall) it falls back to the previous browser-based flow.

🧭 IntersectionObserver (Canary)

As part of the effort to bring web APIs to React Native, 0.83 adds IntersectionObserver support in the canary release. The release notes describe it as a way to asynchronously observe layout intersections between a target element and its ancestor, and mention API/implementation docs plus RNTester examples.

⏱️ Web Performance APIs are now stable

React Native 0.83 rolls out as stable a subset of Web Performance APIs introduced in 0.82. The list includes High Resolution Time (performance.now(), performance.timeOrigin), Performance Timeline (PerformanceObserver and getEntries* methods), User Timing (performance.mark, performance.measure), Event Timing (event entry types), and Long Tasks (longtask entry types). The release notes state these APIs are visible in the DevTools Performance panel and usable at runtime via PerformanceObserver, including in production builds—enabling real-world performance metrics collection.

🧪 Hermes V1 (experimental)

Hermes V1 is described as the next evolution of Hermes, with compiler/VM improvements that significantly boost JavaScript performance. After being introduced as an experimental opt-in in 0.82, Hermes V1 gets further performance improvements in 0.83. The notes also explain that enabling Hermes V1 requires building React Native from source (not compatible with precompiled React Native builds), and provide specific Android/iOS enablement steps plus a runtime check for the Hermes version.

🍎 iOS: compile out Legacy Architecture (experimental)

React Native 0.83 adds an iOS flag (RCT_REMOVE_LEGACY_ARCH=1) to compile out the Legacy Architecture if your app is already on the New Architecture. The notes claim this can reduce build time and app size, and provide example measurements on a new app without dependencies (build time 73.0s → 58.2s; size 51.2MB → 48.2MB), while noting results depend on how many third-party libraries you use. They also state this flag is not compatible with React Native precompiled binaries and requires building from source.

🍏 iOS: debug precompiled binaries (experimental)

The release notes introduce the ability to debug React Native code shipped with a precompiled binary on iOS, primarily for library maintainers or teams building native modules/components. They describe the workflow and emphasize that RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS=1 instructs CocoaPods to download and expand React Native dSYMs so you can step into React Native code in Xcode.

🧯 Production impact and rollout strategy

React Native 0.83 has no user-facing breaking changes and explicitly states that apps on 0.82 should be able to upgrade without app code changes, which supports safer, more frequent upgrades. The DevTools Performance panel plus stable Web Performance APIs (including PerformanceObserver working in production) create a practical path for measuring regressions during gradual rollout instead of relying only on local profiling. The release also ships two Android-specific deprecations—sendRequestInternal (Networking) and startOperationBatch/finishOperationBatch (Animation)—which teams should track across internal code and third-party dependencies.

📚 https://reactnative.dev/blog/2025/12/10/react-native-0.83

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

SwiftUI Window, Scene & Multi-Window Architecture

1 Share

As soon as your app goes beyond a single screen, window and scene management becomes real architecture, not boilerplate.

Most SwiftUI apps accidentally:

  • misuse ScenePhase
  • hard-code navigation per window
  • break state when opening multiple windows
  • duplicate ViewModels
  • fight iPad & macOS behavior
  • misunderstand what a scene actually is

This post explains how SwiftUI really manages windows and scenes, and how to architect apps that work correctly on:

  • iPhone
  • iPad (multi-window)
  • macOS
  • visionOS

🧠 1. App vs Scene (The Most Important Distinction)

App

  • Defines what your app is
  • Owns global configuration
  • Creates scenes

Scene

  • Defines how your app is presented
  • Owns window lifecycle
  • Can exist multiple times simultaneously
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            RootView()
        }
    }
}

Rule:
📌 Your app can have multiple scenes, and each scene can have multiple windows.

🪟 2. What a WindowGroup Really Does

WindowGroup {
    ContentView()
}

This means:

  • iOS: multiple app windows (iPad)
  • macOS: multiple windows
  • visionOS: multiple spatial instances

Each window:

  • has its own view hierarchy
  • has its own state
  • does NOT automatically share ViewModels

⚠️ 3. The Biggest Multi-Window Bug

This is wrong:

@StateObject var vm = GlobalViewModel()

inside a WindowGroup.

Why?

  • Each window gets a new instance
  • State diverges
  • Navigation desyncs

🧱 4. Correct Global State Placement

Global state must live above scenes:

@main
struct MyApp: App {
    @StateObject private var appState = AppState()

    var body: some Scene {
        WindowGroup {
            RootView()
                .environmentObject(appState)
        }
    }
}

Now:

  • all windows share state
  • navigation is consistent
  • data stays in sync

🧭 5. Scene-Local State vs App-Global State

Scene-local:

  • navigation stack
  • selection
  • focus
  • scroll position

App-global:

  • authentication
  • user session
  • cache
  • feature flags
  • deep links

Never mix them.

🔄 6. ScenePhase Is Per-Scene (Not Global)

@Environment(\.scenePhase) var scenePhase

Each window has its own phase.

That means:

  • backgrounding one window ≠ app background
  • inactive ≠ destroyed
  • active ≠ foreground for all windows

Use this wisely.

🧩 7. Supporting Multiple Scene Types

SwiftUI supports multiple scene roles:

var body: some Scene {
    WindowGroup("Main") {
        MainView()
    }

    WindowGroup("Inspector") {
        InspectorView()
    }

    Settings {
        SettingsView()
    }
}

Use cases:

  • inspector panels
  • settings windows
  • auxiliary tools
  • debug overlays

🪟 8. Opening New Windows Programmatically

@Environment(\.openWindow) var openWindow

Button("Open Details") {
    openWindow(id: "details")
}

Define the window:

WindowGroup(id: "details") {
    DetailView()
}

This is how real desktop-class SwiftUI apps work.

🧠 9. Window-Scoped Dependency Injection

Each window should get:

  • its own navigation state
  • shared services
  • shared app state

Example:

WindowGroup {
    RootView(
        router: Router(),
        services: services
    )
}

But:

  • services are shared
  • routers are per-window

🧪 10. Testing Multi-Window Behavior

You must test:

  • opening multiple windows
  • closing windows
  • backgrounding one scene
  • restoring state
  • shared state mutation

Most SwiftUI bugs only appear with two windows open.

🚀 Final Thoughts

SwiftUI scenes are not boilerplate — they are architecture.

Once you understand:

  • app vs scene
  • window identity
  • scene-local vs global state
  • multi-window behavior

You can build apps that feel:

  • native
  • correct
  • scalable
Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

The Writers Write Book Reading Challenge – 3

1 Share

Are you overwhelmed by the sheer number of books to choose from? Here’s a reading challenge to make choosing a book (and reading it!) more fun.

The Writers Write Book Reading Challenge – 3

At Writers Write, we believe in healthy challenges. After all, they push us to the edge of our comfort zone, and we all know that’s where learning begins, and where adventure starts.

We’d like you to start this new book-reading challenge with us. You can begin at any month of the year. For example, let’s say you start in May. Then you simply work your way to the end of the list and continue with January until you reach April. Challenge completed! But of course, we hope you’ll grab a book the minute this post is published.

This is our third book reading challenge. You can read all about our other challenges at the end of this post.

A New Challenge Every Month

  1. January: January is the time of new beginnings. Choose a book about a journey. How about Jules Verne’s Around The World In 80 Days? Or the Outlander series by Diana Gabaldon?
  2. February: Read a love story. This month, you don’t need to feel ashamed for enjoying a cheesy one.
  3. March: Go through your phone contacts or your social media friends. Message the third person, asking for a book recommendation.
  4. April: Honour April Fool’s Day and read a funny book. Yu could get started on one of the two series by Douglas Adams. Choose either the series The Hitchhiker’s Guide To The Galaxy or Dirk Gently’s Holistic Detective Agency. You may, of course, also choose any other book you think is funny.
  5. May: This month, please read a book that was written after the movie. For example, any of the Books on Star Wars. May the force be with you!
  6. June: Read a novel written by an author who uses a pen name. You could choose Robert Galbraith, John Le Carré, Mark Twain, or Ellis Peters, for example.
  7. July: Indulge in a beach read. To find one, go to the next train station or airport. Usually, their bookstores sell lots of beach reads.
  8. August: Read a book set in the desert. Here are two suggestions: T.E. Lawrence’s Seven Pillars Of Wisdom (that’s the story of Lawrence of Arabia), or Michael Ondaatje’s novel The English Patient.
  9. September: Please visit your local library, and ask which novel is most popular, and which is least popular. Look at both and choose one, or both.
  10. October: Time to get a little spooked. Please read a book about a witch. I suggest Deborah Harkness, any one of her All Souls series (the first one is called A Discovery Of Witches). It’s all about witches and vampires.
  11. November: This is the month of Thanksgiving. So, choose a book about gratitude. How about Louisa May Alcott’s Little Women? Or Paulo Coelho’s The Alchemist? Maybe these books can inspire you to keep a gratitude journal.
  12. December: Please gift the joy of reading to children. Maybe you’d like to volunteer to read a Christmas-themed book to kids at your local library? Or at an elementary school? My favourite is Nicholas Allan’s Father Christmas Needs A Wee.

When you’ve completed this challenge, pat yourself on the shoulder!  We know that life is busy, and it can be hard to find the time for reading. Yet you have made time and space for one of the greatest hobbies in the world. It’s also a fundamental activity if you want to be a writer.

The Last Word

If you’ve enjoyed this challenge, you can continue with more challenges! Please look at our previous ones:

  1. The Writers Write Book Reading Challenge
  2. The Writers Write Book Reading Challenge 2

Image: Pixabay

Susanne Bennett

By Susanne Bennett. Susanne is a German-American writer who is a journalist by trade and a writer by heart. After years of working at German public radio and an online news portal, she has decided to accept challenges by Deadlines for Writers. Currently she is writing her first novel with them. She is known for overweight purses and carrying a novel everywhere. Follow her on Facebook.

More Posts From Susanne

  1. 20 Weird Things Readers Do
  2. The 5 Most Common Themes In Literature
  3. What’s A Golden Shovel Poem? & How Do I Write One?
  4. How Your Writing Tool Shapes Your Text
  5. Why Writers Should Write By Hand
  6. What Is A Parody & How Do I Write One?
  7. The 7 Pillars Of Historical Fiction
  8. How To Travel Like A Writer
  9. What Is Dystopian Fiction? & How Do I Write It?
  10. Impostor Syndrome – What It Is And How To Get Over It

Top Tip: Find out more about our workbooks and online courses in our shop.

The post The Writers Write Book Reading Challenge – 3 appeared first on Writers Write.

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories