# How to Start Debugging Unfamiliar Code in the jFrame Go Framework

> Learn to debug unfamiliar code in the jFrame Go Framework. Start by exploring the kernel package and tracing the four-phase lifecycle to understand dependency flow and get started quickly.

- Repository: [卷鸡科技/jframe](https://github.com/juanjitech/jframe)
- Tags: how-to-guide
- Published: 2026-03-05

---

**Start by locating the kernel package in [`core/kernel/engine.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/engine.go) and [`core/kernel/module.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/module.go), then trace the four-phase lifecycle (Init, Load, Start) through the example module to understand how dependencies flow through the injector.**

When you inherit an unfamiliar Go codebase like **jFrame** (github.com/juanjitech/jframe), debugging effectively requires mapping the architectural backbone before diving into specific bugs. The framework follows a modular, dependency-injected design where every component revolves around a central **kernel** that orchestrates startup and shutdown. By understanding this contract first, you transform opaque stack traces into readable roadmaps of where configuration, initialization, or runtime logic fails.

## Locate the Kernel: Your Entry Point for Debugging Unfamiliar Code

The kernel package acts as the central nervous system of jFrame. Every debugging session should begin here because it defines how modules are born, wired together, and executed.

### The Engine Orchestrator ([`core/kernel/engine.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/engine.go))

The `Engine` struct in [`core/kernel/engine.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/engine.go) is the primary runtime container. It initializes the **dependency injector**, creates a cancellable context for graceful shutdown, and manages the lifecycle of every registered module. When debugging startup failures, inspect the `New()` function to verify default configurations and the `StartModule()` method to see how the engine sequences module initialization.

### The Module Contract ([`core/kernel/module.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/module.go))

All functionality in jFrame extends the `Module` interface defined in [`core/kernel/module.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/module.go). This interface mandates methods like `Name()`, `Config()`, `Init()`, `Load()`, and `Start()`. The file also provides `UnimplementedModule`, a helper struct that modules embed to satisfy the interface with no-ops. When you encounter a type assertion panic or missing method error, verify that the module correctly embeds this base struct.

## Understand the Four-Phase Lifecycle Before Debugging

jFrame modules progress through a strict, ordered lifecycle. Understanding these phases helps you isolate whether a bug is a configuration error (phase 1), a dependency wiring issue (phases 2-3), or a runtime logic flaw (phase 4).

1. **Configuration Loading** – The engine calls each module’s `Config()` method and unmarshals Viper configuration into a dynamically generated struct. Silent failures here usually stem from mismatched YAML keys or struct tags.
2. **PreInit → Init → PostInit** – These sequential hooks allow modules to register dependencies in the injector (`h.Map`), perform setup, and finalize configuration. Panics here often indicate missing dependencies or circular references.
3. **Load** – Modules retrieve required services (e.g., the HTTP router `*jin.Engine`) from the injector using `h.Value`, `h.Invoke`, or `h.Load`. This is where type mismatches surface.
4. **Start** – Each module runs its own goroutine. The engine waits for a graceful shutdown signal. Blocking operations or unhandled panics here will halt the entire service.

## Trace Real Data Flow Through the Example Module

Concrete examples anchor abstract lifecycle concepts. The **example module** at [`mod/example/mod.go`](https://github.com/juanjitech/jframe/blob/main/mod/example/mod.go) demonstrates the canonical pattern for dependency registration and retrieval.

### Registering Dependencies with the Hub

During the `Init` phase, modules expose values through the dependency injector (referred to as the `Hub`). The example module registers a simple string:

```go
func (m *Mod) Init(h *kernel.Hub) error {
    // expose a string through the injector
    h.Map("hello world")
    return nil
}

```

When debugging "dependency not found" errors, verify that the providing module’s `Init` method successfully calls `h.Map` with the correct type.

### Retrieving Dependencies in Load Phase

The `Load` phase demonstrates three equivalent methods for dependency retrieval:

```go
func (m *Mod) Load(h *kernel.Hub) error {
    // Method 1: Direct type-based lookup
    s1 := h.Value(reflect.TypeOf("")).String()
    
    // Method 2: Callback injection
    h.Invoke(func(s string) { 
        fmt.Println("via Invoke:", s) 
    })
    
    // Method 3: Pointer loading
    var s2 string
    h.Load(&s2)
    
    fmt.Println(s1, s2)
    return nil
}

```

If a module panics during `Load`, check that the type requested matches exactly what was registered in `Init`—the injector uses strict type matching.

## Systematic Debugging Checklist for Unfamiliar Go Code

When a bug surfaces in jFrame, use this structured approach to isolate the failure point. Each step maps to specific source files and method signatures.

| Step | What to Inspect | Why It Matters |
|------|----------------|----------------|
| **Engine creation** | `New` in [`core/kernel/engine.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/engine.go) | Confirms config defaults, injector setup, and context initialization |
| **Module registration** | `RegMod` in [`core/kernel/module.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/module.go) | Guarantees unique names and correct module ordering |
| **Config unmarshalling** | `StartModule` block that builds a dynamic struct | Mis-named Viper keys cause silent failures where modules receive zero values |
| **Dependency map** | Calls to `h.Map`, `h.Value`, `h.Load` inside modules | Missing or wrongly typed values trigger panics during `Load` or `Start` |
| **Runtime errors** | `Start` goroutine bodies in module implementations | Anything that blocks or panics here stops the whole service; check for unhandled errors in goroutines |

When you encounter a stack trace, follow it back from the panic site to the module's implementation, then verify that the kernel properly injected all required dependencies before the failing method was called.

## Summary

- **Start at the kernel**: [`core/kernel/engine.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/engine.go) and [`core/kernel/module.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/module.go) define the runtime contract and lifecycle hooks that every jFrame module follows.
- **Follow the four phases**: Configuration → Init → Load → Start. Most bugs reveal themselves as configuration mismatches, missing dependencies, or runtime panics in these distinct stages.
- **Trace the injector**: The `Hub` methods (`Map`, `Value`, `Load`, `Invoke`) wire components together. Type mismatches here are the most common source of `Load`-phase panics.
- **Use the example module**: [`mod/example/mod.go`](https://github.com/juanjitech/jframe/blob/main/mod/example/mod.go) provides a working reference for how dependencies flow through a real implementation.

## Frequently Asked Questions

### What is the first file to check when debugging unfamiliar Go code in jFrame?

Start with [`core/kernel/engine.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/engine.go). This file contains the `Engine` struct and its `New()` constructor, which initializes the dependency injector and configuration context. Understanding how the engine orchestrates the startup sequence provides the necessary context to interpret stack traces from any other part of the codebase.

### How does jFrame's dependency injection help with debugging?

The explicit `Hub` interface in [`core/kernel/module.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/module.go) forces dependencies to be declared in `Init` (via `h.Map`) and retrieved in `Load` (via `h.Value` or `h.Load`). This separation makes it easy to isolate whether a failure stems from a missing registration (check `Init`) or a type mismatch (check `Load`), rather than hunting through global state or hidden imports.

### Where do runtime panics usually originate in jFrame modules?

Most runtime panics occur in the `Load` or `Start` phases. During `Load`, panics typically indicate that `h.Value` or `h.Load` was called for a type that was never registered in `Init`. During `Start`, panics usually happen inside the module's goroutine where actual business logic runs; unhandled errors here can crash the entire service because the engine waits for all modules to shut down gracefully.

### How do I add logging to debug a custom module in jFrame?

Register a logger instance in your module's `Init` method using `h.Map`, then retrieve it in `Load` or `Start` via `h.Load`. Because the kernel guarantees that `Init` runs before `Load`, you can safely assume the logger is available when your module starts its goroutine. Alternatively, inspect the engine's configuration loading in [`core/kernel/engine.go`](https://github.com/juanjitech/jframe/blob/main/core/kernel/engine.go) to ensure your module's `Config()` struct tags match the YAML keys in your configuration file.