How to Find the Entry Point of Any Repository, in Any Stack

Every codebase starts somewhere the outside world touches it. This is a field guide to finding main functions, routes, CLI commands, workers and build entry points in any stack.

By The OpenVisio Team · · 11 min read

The fastest way into an unfamiliar codebase is through its front doors. An entry point is any place where the outside world causes code to run: a process starting, an HTTP request arriving, a command typed at a shell, a message landing on a queue, a timer firing, a user clicking a button. Find those, and you can follow execution outward into the rest of the system. Start anywhere else, and you are reading helpers that make no sense without their callers.

Most repositories have more than one entry point, and the important ones are not always where you expect. This guide covers how to find each kind, with search commands for common stacks. It pairs with the broader method in how to read an unfamiliar codebase, where finding the entry point is step three.

Key takeaways

  • Start with what the repo produces: run scripts, Dockerfiles, CI deploy jobs and package manifests list every process and its start command.
  • The start command names the process entry point. Follow it to main, the server bootstrap, or the framework's convention.
  • HTTP entry points are routes. Find the router or the framework's file-based routing convention and list them all.
  • Background entry points (workers, consumers, cron) are easy to miss and often hold critical logic. Search for queue, schedule and worker registrations.
  • Libraries have API entry points, not process ones. Look at exports, __init__.py, lib.rs or the public package.
  • Build config is an entry point for understanding: it defines what gets compiled, bundled and shipped.

Start with what the repo runs

Before searching source, find the commands that start things. These files list them:

FileWhat it tells you
package.json (scripts, main, bin, exports)Dev and start commands, CLI binaries, library entry
Dockerfile (CMD, ENTRYPOINT)Exactly what runs in production
docker-compose.ymlEvery process and its dependencies
ProcfileProcess types on Heroku-style platforms (web, worker, release)
Makefile, justfile, Taskfile.ymlBuild and run targets
CI config (.github/workflows/*.yml)What gets built, tested and deployed
Kubernetes or Helm manifestsDeployed workloads, including cron jobs
pyproject.toml ([project.scripts])Python CLI entry points
Cargo.toml ([[bin]], src/main.rs)Rust binaries
go.mod plus cmd/ foldersGo binaries, one per subfolder by convention

A quick sweep:

# Node: scripts and binaries
jq '.scripts, .bin, .main, .exports' package.json

# What production actually runs
rg -n "^(CMD|ENTRYPOINT)" --glob 'Dockerfile*'

# Every process in compose
yq '.services | keys' docker-compose.yml

# Python console scripts
rg -n -A5 "\[project.scripts\]" pyproject.toml

The production start command is the most trustworthy fact in the repo. Documentation drifts; the CMD line in the Dockerfile that ships is what actually runs.

Process entry points by language

Once you know the start command, follow it to the first line of code that runs.

Go. Every binary has a package main with func main(). By convention, each binary lives in cmd/<name>/main.go.

rg -l "^package main$" --type go

main in Go programs usually does wiring: load config, build dependencies, register routes, start servers. Read it top to bottom; it is a table of contents for the process.

Node and TypeScript. The start script names a file (node dist/server.js, tsx src/index.ts). For frameworks, the convention replaces the file: Next.js uses the app/ or pages/ directory, NestJS bootstraps in src/main.ts with NestFactory.create, Express apps usually have a file that calls app.listen.

rg -n "\.listen\(|createServer\(|NestFactory\.create" --type ts --type js

Python. Look for if __name__ == "__main__":, the [project.scripts] table, manage.py for Django, a wsgi.py or asgi.py that defines the application object, or the module passed to gunicorn, uvicorn or celery in the start command.

rg -n "__name__ == .__main__.|FastAPI\(|Flask\(__name__\)|get_wsgi_application|get_asgi_application" --type py

Java and Kotlin. Search for public static void main or, in Spring Boot, @SpringBootApplication. Spring wires most things through annotations, so the main class is short; the real entry points are controllers and listeners.

Rust. src/main.rs for the default binary, src/bin/*.rs for additional ones, src/lib.rs for the library crate. In a workspace, check each member's Cargo.toml.

Ruby on Rails. config.ru boots the Rack app; config/routes.rb lists HTTP entry points; bin/rails and lib/tasks/*.rake define commands.

HTTP entry points: routes

For a web service, each route is an entry point. Get the full list early. Many frameworks can print it for you:

# Rails
bin/rails routes

# Django (requires django-extensions)
python manage.py show_urls

# Laravel
php artisan route:list

# Spring Boot with Actuator mappings endpoint enabled
curl localhost:8080/actuator/mappings

When there is no command, search for the registration pattern:

# Express, Fastify, Koa-router style
rg -n "\.(get|post|put|patch|delete)\(['\"]/" --type ts --type js

# FastAPI and Flask decorators
rg -n "@(app|router|bp)\.(get|post|put|delete|route)\(" --type py

# Go net/http and popular routers
rg -n "HandleFunc\(|\.Handle\(|r\.(Get|Post|Put|Delete)\(" --type go

# Spring
rg -n "@(Get|Post|Put|Delete|Request)Mapping" --type java --type kotlin

For file-based routing frameworks, the folder tree is the route table. In the Next.js App Router, every page.tsx is a page and every route.ts is an API handler; folders in parentheses like (marketing) are route groups that do not appear in the URL. Also check for middleware (middleware.ts in Next.js, app.use in Express, MIDDLEWARE in Django settings), which runs before every matching route and often holds authentication and redirects.

Once you have the route list, pick the one most relevant to your task and follow it. The full walk from route to database is in tracing a request through a web app.

Background entry points: workers, consumers and schedules

Background work is the most commonly missed entry point. It runs in a separate process, nobody visits it in a browser, and it often holds important logic: billing runs, email sending, data syncs, cleanup jobs.

# Queue workers and consumers
rg -n "new Worker\(|\.process\(|@celery\.task|@shared_task|@app\.task|sidekiq_options|perform_later|@KafkaListener|@RabbitListener|\.subscribe\(" 

# Scheduled jobs
rg -n "cron|schedule\.every|@Scheduled|beat_schedule|CronJob|node-cron|setInterval\(" -i

# Kubernetes scheduled workloads
rg -n "kind: CronJob" --glob '*.y*ml'

Also check platform config outside the code: vercel.json cron entries, cloud scheduler definitions in Terraform, GitHub Actions workflows with schedule: triggers. A job defined in infrastructure code is still an entry point into your application.

CLI entry points

For command-line tools, each subcommand is an entry point. Find where commands are registered:

# Node: commander, yargs, oclif
rg -n "\.command\(|yargs|class .* extends Command" --type ts --type js

# Python: click, typer, argparse
rg -n "@click\.(command|group)|typer\.Typer\(|add_subparsers\(" --type py

# Go: cobra
rg -n "&cobra\.Command\{" --type go

# Rust: clap derive
rg -n "#\[derive\(.*Parser|#\[command\(" --type rust

Running the tool's help output is often faster than reading the registration code: mytool --help and mytool <subcommand> --help give you the command tree, and the command name is a good search term to find its handler.

Frontend entry points

In a single-page app, the process entry is the file that mounts the root component, often src/main.tsx or src/index.tsx with createRoot(...).render(<App />). The bundler config names it: look for input in vite.config.ts (Vite defaults to index.html and follows its script tag), entry in webpack.config.js.

The more useful entry points in a frontend are user actions. Search for the visible text of the button or form you care about, find the component, and follow its event handler to the API call. The API path then leads you to the backend route. That chain of literal searches crosses the whole stack in a few minutes.

Library entry points

A library has no process to start; its entry point is its public API. Look at:

  • package.json exports and main fields in JavaScript
  • __init__.py re-exports in Python packages
  • src/lib.rs and its pub use statements in Rust
  • Exported (capitalized) identifiers in a Go package's root
  • The index.ts barrel file of each package in a TypeScript monorepo

The public API tells you what the library promises. Read the exported functions and types first, then follow each into its implementation as needed.

Build config as an entry point for understanding

Build configuration is not executed by users, but it defines what exists at runtime. tsconfig.json paths, next.config.mjs, vite.config.ts, Bazel BUILD files, build.rs in Rust, and Gradle scripts can add code generation, aliases, environment-specific files and plugins that change what runs. If imports look impossible (@/lib/foo with no lib folder at the root), the answer is in the build config. Read it early, at least to know what aliases and generated files exist.

A checklist for any repo

[ ] Production start command(s) found (Dockerfile CMD, Procfile, deploy config)
[ ] Every deployable process listed (web, workers, schedulers, CLIs)
[ ] Process main / bootstrap file read top to bottom
[ ] Full route list obtained (framework command or search)
[ ] Global middleware identified
[ ] Background workers and consumers listed with their queues/topics
[ ] Scheduled jobs listed, including ones defined in infra or CI
[ ] Frontend mount point and bundler entry identified
[ ] Build aliases and code generation steps noted

Letting a map do the first pass

Searching by hand works everywhere, but on a large repo the first pass takes a while. A structural map can shorten it: when a repo's import graph is laid out, entry points tend to stand out as the nodes that import a lot and are imported by nothing. OpenVisio's Explore space shows that architecture view next to the file tree for any GitHub repo, and coding agents can ask the same question over MCP with tools like get_repo_skeleton, as described in an MCP server for codebase context.

Whichever route you take, finish by writing the list down: every process, every route group, every worker and every schedule, each with the file that defines it. That list is the index for everything else you will read.

entry pointsreading codecodebase navigationbuild tools

See it on your own repo

Paste a GitHub URL and get the day-one walkthrough — free, in your browser.

Index a repo

Keep reading