16-Week Plan: From Curious Reader to OpenSearch Maintainer Candidate
This is a 16-week, ~10-hour-per-week plan that maps the curriculum (Levels 1–9 plus a 2-week capstone) onto a calendar. Each week states:
- Reading — concrete OpenSearch source files. Open them; do not just skim diagrams.
- Hands-on — what you must build/run on your machine (
./gradlew,curl,git). - GitHub issue practice — real search queries against
opensearch-project/OpenSearchthat surface beginner-appropriate issues. OpenSearch tracks everything on GitHub, not JIRA. - Labs — the curriculum labs you must complete.
- Exit checkpoint — concrete deliverables. If you cannot produce them, repeat the week.
The plan assumes you have ~/src/OpenSearch checked out, ./gradlew run serving :9200, a passing
./gradlew :server:test --tests "...ClusterStateTests", and a working JDK 21 / Git environment from
the prerequisites and the warm-up.
How to run a GitHub issue query: paste it into the search box at
https://github.com/opensearch-project/OpenSearch/issues, or build a URL:https://github.com/opensearch-project/OpenSearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22. Labels with spaces must be quoted:label:"good first issue".
Weeks 1–2: Level 1 — Lucene & OpenSearch Foundation
Week 1 — Build, run, and the data model
Reading
README.md,CONTRIBUTING.md,DEVELOPER_GUIDE.md,TESTING.mdin the repo root.server/src/main/java/org/opensearch/node/Node.java— how a node wires up every service (readNode(...)constructor top-to-bottom; do not memorize, just map the territory).server/src/main/java/org/opensearch/index/shard/IndexShard.java— read the class javadoc and the field declarations only this week.
Hands-on
- Build:
./gradlew localDistrothen./gradlew run. - Reproduce all five warm-up scenarios against
:9200(bulk ingest,match,date_histogram, multi-node, snapshot). - Inspect a real shard on disk:
findthenodes/0/indicesdata dir under the./gradlew runworking tree and list the Lucene segment files.
GitHub issue practice
is:issue is:open label:"good first issue"
is:issue is:open label:"good first issue" label:"untriaged"
is:issue is:open label:documentation
Labs
Exit checkpoint
- You can build from a clean checkout and serve
:9200. - You can state the OpenSearch-vs-Lucene boundary (segments, DocValues, translog, cluster state).
- You have one
good first issueopen in a browser tab and have read it end-to-end (description + every comment).
Week 2 — First cluster, first index, Lucene underneath
Reading
server/src/main/java/org/opensearch/index/engine/InternalEngine.java— class javadoc + theindex(...)method signature.server/src/main/java/org/opensearch/index/mapper/— skimMapperService,DocumentMapper,KeywordFieldMapper,TextFieldMapper.
Hands-on
- Create an index with explicit mappings; index docs; run
_search,_explain, and_analyze. ./gradlew :server:test --tests "org.opensearch.index.engine.InternalEngineTests"and read 5 of the test methods it ran.
GitHub issue practice
is:issue is:open label:"good first issue" label:"Indexing"
is:issue is:open "analyzer" label:enhancement
Labs
- Lab 1.3 — Launch a Single-Node Cluster and Index Data
- Lab 1.4 — Project: Build a Minimal Lucene Index
Exit checkpoint
- You can write a mapping and explain each field type's Lucene representation.
- You can run a single test method by name without consulting docs.
- You can describe what
_analyzereturns and why.
Weeks 3–4: Level 2 — Contributor Onboarding (GitHub, PRs, DCO)
Week 3 — Repository structure and the contribution flow
Reading
MAINTAINERS.md,CHANGELOG.md,.github/pull_request_template.md.- Top-level
build.gradleandsettings.gradle— how modules are declared. - The SPDX header on any file in
server/src/main/java/org/opensearch/.
Hands-on
- Fork the repo on GitHub; add your fork as a remote; create a branch.
- Configure DCO:
git config user.name/user.email, thengit commit -sand verify theSigned-off-by:trailer. - Run
./gradlew precommitand read what it checks (checkstyle, forbidden APIs, license headers).
GitHub issue practice
is:pr is:merged label:"good first issue"
is:issue is:open label:"help wanted" label:documentation
is:pr is:open review:required
Labs
- Lab 2.1 — Navigate the OpenSearch Repository Structure
- Lab 2.2 — Prepare a PR Using OpenSearch Practices
Exit checkpoint
- You can explain the full PR lifecycle: fork → branch →
commit -s→ CHANGELOG entry → PR → DCO/precommit checks → review → backport label. - You can run
./gradlew precommitand interpret a failure. - You can name what every PR must include (test, CHANGELOG entry, sign-off).
Week 4 — Tests as documentation; your first fix
Reading
test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java(skim).- One real merged "good first issue" PR diff (open it on GitHub; read the conversation too).
Hands-on
- Add a trivial no-op test to
serverand run it via./gradlew :server:test --tests "...". - Reproduce a documentation or error-message issue locally.
GitHub issue practice
is:issue is:open label:"good first issue" -label:"untriaged"
is:issue is:open label:bug label:"good first issue" sort:created-desc
Labs
Exit checkpoint
- You can produce a signed, CHANGELOG-bearing commit that passes
./gradlew precommit. - You can review a real PR and list three things its author should improve.
- You have drafted (not necessarily submitted) a comment on a real
good first issue.
Weeks 5–6: Level 3 — Architecture (REST → Transport → Action)
Week 5 — The request path
Reading
server/src/main/java/org/opensearch/rest/RestController.java.server/src/main/java/org/opensearch/rest/action/search/RestSearchAction.java.server/src/main/java/org/opensearch/action/ActionModule.java— how actions are registered.
Hands-on
- Start
./gradlew run; send a_searchand trace it: set a breakpoint inRestSearchActionand inTransportSearchAction, step from HTTP to action. grep -rn "registerHandler\|registerAction" server/src/main/java/org/opensearch/action/ActionModule.java.
GitHub issue practice
is:issue is:open label:"good first issue" label:">enhancement"
is:issue is:open "RestController" in:title,body
is:pr is:merged path:server/src/main/java/org/opensearch/rest
Labs
- Lab 3.1 — Trace a REST Request from HTTP to TransportAction
- Lab 3.2 — Node Roles, Discovery, and the Transport Layer
Exit checkpoint
- You can name the chain
RestController→RestHandler→NodeClient.execute→TransportActionand cite each file. - You can explain the difference between
HandledTransportAction,TransportSingleShardAction,TransportBroadcastAction,TransportReplicationAction, andTransportClusterManagerNodeAction.
Week 6 — Transport, threadpools, and a custom REST action
Reading
server/src/main/java/org/opensearch/transport/TransportService.java.libs/core/src/main/java/org/opensearch/core/common/io/stream/StreamInput.java/StreamOutput.javaandWriteable.server/src/main/java/org/opensearch/threadpool/ThreadPool.java(theNamesconstants).
Hands-on
greptheThreadPool.Namesconstants and find whereSEARCHandWRITEpools are used.- Build the custom REST action plugin in Lab 3.3 and load it into
./gradlew run.
GitHub issue practice
is:issue is:open "transport" label:bug
is:issue is:open label:"good first issue" "thread pool" in:title,body
Labs
Exit checkpoint
- You can explain how a
TransportRequestis serialized (Writeable/StreamOutput,NamedWriteableRegistry). - You have a working custom REST action plugin that responds on
:9200. - You can name three thread pools and the work each handles.
Weeks 7–8: Level 4 — Cluster Coordination and State
Week 7 — The Coordinator and cluster-manager election
Reading
server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java.server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java,JoinHelper.java,PreVoteCollector.java.server/src/main/java/org/opensearch/cluster/ClusterState.java—Metadata,RoutingTable,DiscoveryNodes,ClusterBlocks.
Hands-on
- Run
./gradlew run -PnumNodes=3. Inspect_cluster/state,_cat/nodes,_cat/shards. - Kill the cluster-manager node; watch a re-election; confirm via
_cluster/health.
GitHub issue practice
is:issue is:open "cluster manager" in:title,body
is:issue is:open label:"good first issue" "cluster state" in:title,body
is:issue is:open "election" label:bug
Labs
- Lab 4.1 — Read the Coordinator and Cluster-Manager Election
- Lab 4.2 — ClusterState, Publishing, and the Applier Model
Exit checkpoint
- You can explain the two-phase publish/commit and which class publishes (
PublicationTransportHandler) vs. applies (ClusterApplierService). - You can name the four components of
ClusterStateand what each holds. - You can describe what happens to writes during a cluster-manager re-election.
Week 8 — Cluster state updates, applier model, and allocation
Reading
server/src/main/java/org/opensearch/cluster/service/MasterService.java(cluster-manager service),ClusterApplierService.java,ClusterService.java.server/src/main/java/org/opensearch/cluster/routing/allocation/AllocationService.java.server/src/main/java/org/opensearch/cluster/routing/allocation/decider/— pick 4 deciders.
Hands-on
- Force an
UNASSIGNEDshard (setnumber_of_replicashigher than node count); inspect with_cluster/allocation/explain. - Implement the custom
ClusterStateListener(Lab 4.3); log every state version change.
GitHub issue practice
is:issue is:open "allocation" label:bug
is:issue is:open "AllocationDecider" in:title,body
is:issue is:open label:"good first issue" "unassigned shard" in:title,body
Labs
Exit checkpoint
- You can read
_cluster/allocation/explainoutput and name the decider that blocked an allocation. - You can write a
ClusterStateListenerand explain when it fires. - You can trace a
ClusterStateUpdateTaskthroughMasterServiceto a published state.
Weeks 9–10: Level 5 — Testing and Debugging
Week 9 — The test framework and InternalTestCluster
Reading
test/framework/src/main/java/org/opensearch/test/OpenSearchIntegTestCase.java.test/framework/src/main/java/org/opensearch/test/InternalTestCluster.java.test/framework/src/main/java/org/opensearch/test/OpenSearchSingleNodeTestCase.java.
Hands-on
- Run
./gradlew :server:internalClusterTest --tests "org.opensearch.cluster.*". - Reproduce a randomized failure deliberately: run a test, capture the
-Dtests.seed=...line, re-run with that seed.
GitHub issue practice
is:issue is:open label:flaky-test
is:issue is:open label:flaky-test label:"untriaged"
is:issue is:open label:flaky-test sort:created-desc
Labs
Exit checkpoint
- You can write an
OpenSearchIntegTestCasetest that spins up a multi-node cluster in-JVM. - You can reproduce a randomized failure from its printed seed.
- You can distinguish a unit test from an
*ITintegration test by what it extends.
Week 10 — Flaky tests and serialization round-trips
Reading
test/framework/src/main/java/org/opensearch/test/AbstractWireSerializingTestCase.java.- An existing
*Testsclass using@AwaitsFix(grep for it).
Hands-on
- Pick a
flaky-test-labeled issue; reproduce the failure by running its test on a loop with random seeds. - Write an
AbstractWireSerializingTestCaseround-trip for aWriteableof your choice.
GitHub issue practice
is:issue is:open label:flaky-test no:assignee
is:pr is:merged "AwaitsFix" in:title,body
is:issue is:open "reproduce" label:flaky-test
Labs
Exit checkpoint
- You can mute a flaky test correctly (
@AwaitsFix(bugUrl=...), never@Ignore). - You can write a wire-serialization round-trip test for BWC confidence.
- You can take a
flaky-testissue from repro to a candidate fix.
Weeks 11–12: Level 6 — Indexing Path and Storage Engine
Week 11 — The write path to Lucene
Reading
server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java.server/src/main/java/org/opensearch/index/shard/IndexShard.java—applyIndexOperationOnPrimary(...).server/src/main/java/org/opensearch/index/engine/InternalEngine.java—index(...).
Hands-on
- Index a doc with a breakpoint in
IndexShard.applyIndexOperationOnPrimaryand step intoInternalEngine.index→ LuceneIndexWriter. - Force a
_refreshand a_flush; observe segment file changes on disk.
GitHub issue practice
is:issue is:open "InternalEngine" in:title,body
is:issue is:open label:bug "translog" in:title,body
is:issue is:open label:"good first issue" "indexing" in:title,body
Labs
- Lab 6.1 — Trace a Document Index Request to Lucene
- Lab 6.2 — IndexShard, InternalEngine, and the Translog
Exit checkpoint
- You can walk indexing end-to-end: action →
IndexShard→InternalEngine→IndexWriter+Translog. - You can explain refresh (visibility) vs. flush (durability) vs. merge (cleanup).
- You can describe how a write replicates (document vs. segment replication).
Week 12 — Mapping, analysis, and a custom analyzer
Reading
server/src/main/java/org/opensearch/index/mapper/MapperService.java,DocumentMapper.java,TextFieldMapper.java.modules/analysis-common/src/main/java/org/opensearch/analysis/common/— pick two filters.
Hands-on
- Build the custom analyzer plugin (Lab 6.3); register it; verify with
_analyze. grepfor an existingTokenFilterFactoryand mirror its structure.
GitHub issue practice
is:issue is:open "analyzer" label:enhancement
is:issue is:open label:"good first issue" "tokenizer" in:title,body
is:pr is:merged path:modules/analysis-common
Labs
Exit checkpoint
- You can implement an
AnalysisPluginexposing a custom filter and prove it via_analyze. - You can explain how a mapping field type chooses its analyzer and Lucene field.
Weeks 13–14: Level 7 — Search Path and Aggregations
Week 13 — Query and fetch phases
Reading
server/src/main/java/org/opensearch/action/search/TransportSearchAction.java.server/src/main/java/org/opensearch/search/SearchService.java,search/query/QueryPhase.java,search/fetch/FetchPhase.java.server/src/main/java/org/opensearch/action/search/SearchPhaseController.java(the reduce).
Hands-on
- Trace a
_searchfromTransportSearchActionfan-out to per-shardQueryPhaseto the coordinating-node reduce with breakpoints. - Run the same query with
?explain=trueand read the BM25 explanation.
GitHub issue practice
is:issue is:open "QueryPhase" in:title,body
is:issue is:open label:bug "search" label:"good first issue"
is:pr is:merged path:server/src/main/java/org/opensearch/search
Labs
Exit checkpoint
- You can name and order the phases: (DFS) → query → fetch, and where the reduce happens.
- You can explain what
SearchContext/DefaultSearchContexthold per shard.
Week 14 — Aggregations and a custom aggregation
Reading
server/src/main/java/org/opensearch/search/aggregations/AggregatorFactory.java,AggregatorBase.java.search/aggregations/bucket/histogram/DateHistogramAggregator.java,bucket/terms/TermsAggregator.java.- Any
InternalAggregation.reduce(...)implementation.
Hands-on
- Run
date_histogram+termssub-agg (warm-up Scenario 3); break in the aggregator and the reduce. - Build the custom aggregation (Lab 7.3); wire it via
SearchPlugin.
GitHub issue practice
is:issue is:open "aggregation" label:bug
is:issue is:open label:"good first issue" "aggregation" in:title,body
is:issue is:open "doc_count_error_upper_bound" in:title,body
Labs
Exit checkpoint
- You can explain the
AggregatorFactory→Aggregator→InternalAggregation.reducelifecycle. - You can explain why aggregations read DocValues, not the inverted index.
- You have a working custom aggregation registered via
SearchPlugin.
Week 15: Level 8 — Real Issue Contribution (and into the capstone)
Weeks 15–16 overlap with the capstone. Use Week 15 for Level 8's labs and to select and reproduce your capstone issue; use Week 16 to implement, test, and submit.
Reading
- A real open
bugissue's linked code paths (follow the stack trace intoserver/). - Level 8 overview and the issue-roadmap stages you are targeting.
Hands-on
- Reproduce a real GitHub issue locally with a failing test or a
curlrepro. - Localize the root cause to a file:method; draft the fix and a regression test.
GitHub issue practice
is:issue is:open label:bug label:"good first issue" no:assignee
is:issue is:open label:"help wanted" -label:"untriaged" sort:reactions-desc
is:issue is:open label:bug "stack trace" in:body sort:created-desc
Labs
- Lab 8.1 — Reproduce an Existing GitHub Issue
- Lab 8.2 — Implement the Fix, Write the Test, Open the PR
- Lab 8.3 — Improve Error Messages and Diagnostics
Exit checkpoint
- You have a reproducible failing case for a real issue (test or curl).
- You have a root cause localized to a file:method with a one-paragraph explanation.
- You have selected your capstone issue and confirmed it is unassigned and in scope.
Week 16: Level 9 + Capstone — Maintainer-Level Concerns and Shipping
Reading
qa/BWC tests andserver/.../Version.java— how versions gate wire/index compatibility.- Level 9 overview, the compatibility mindset, and the release process.
Hands-on
- Run
./gradlew :server:test+./gradlew precommiton your capstone branch; fix every failure. - Write/extend a BWC or wire-serialization test where your change touches serialization.
- Open the PR: signed commits, CHANGELOG entry, filled-out PR template, linked issue.
GitHub issue practice
is:issue is:open label:"backport 2.x"
is:pr is:open label:"backport 2.x"
is:issue is:open label:v3.0.0
Labs
Capstone — follow capstone/index.md start to finish:
- Issue selection (done in Week 15) → reproduction → execution-path analysis.
- Root cause → implementation → testing → validation.
- PR preparation (DCO sign-off, CHANGELOG, PR template) → GitHub documentation → write-up.
Exit checkpoint
- A real PR opened against
opensearch-project/OpenSearch(or a complete, review-ready patch on a branch), with passing./gradlew precommitand tests on the affected module. - A 1500–3000 word public write-up of the experience (see capstone step 10).
- At least one round of self-review against the PR quality checklist.
How to Use This Plan When You Fall Behind
- If you finish a week's reading but cannot pass the exit checkpoint, repeat the week. Do not advance. The milestones are the real gate; the calendar is a suggestion.
- If a GitHub issue query returns nothing useful, change the query. Labels and triage state shift
constantly — drop a label, widen the date sort, or try
is:issue is:open no:assigneeplus a keyword. The community moves; your queries should too. - Treat
./gradlew precommitand a green:server:teston your touched module as non-negotiable before you call a week done. - Skip a Level only if you can pass all exit checkpoints from the previous Levels in one sitting, and only if a milestone explicitly permits it.
- The two highest-leverage habits, sustained every week: (1) keep one real GitHub issue open and read
it fully, comments included; (2) keep
./gradlew runalive and run at least one breakpoint-driven trace of the week's subsystem.