Back to Explorer
Beginner Friendly Open-Source Ocaml

Good First Issues in Ocaml

Explore curated starter issues in high-scoring Ocaml repositories. Every listed issue belongs to a welcoming repository scored by C-Rank™ on PR merge rates, review responsiveness, and first-timer acceptance.

Updated: Thu, 15 Oct 2026 UTC (Live GitHub Sync)
Real-Time Radar

Arm Issue Sniper for fresh Ocaml beginner issues

Beginner issues in welcoming S/A-Tier repositories get snatched within 15 minutes. Receive instant alerts on Discord, Telegram, or Email the minute a maintainer opens a new task.

Live Good First Issues (24)

Real-Time Open GitHub Issues
stan-dev/stanc3162 Tier A · 62 pts
#1736Opened 16 days ago
Have a different `symN__` generator per optimization pass

Problem Right now each of our optimization passes uses the same symbol generator which builds sym1__, sym2__, sym3__, symN__. When there is a change in the optimizer you end up with a bunch of sym*__ changes and it is very hard to grep and grock when and why those changes happened. Possible Answer We should have a symbol name generator that operates per optimization. So for each pass we would generate sym{PASS_NAME}1__, ..., sym{PASS_NAME}N__. I think this will make it easier to read what caused a change and where.

good first issuecleanupoptimization
1
#2135Opened 1 month ago
Use modulo constants as widening thresholds

Astrée apparently found it to be useful to use the constants (minus 1) in assignments such as x = x % 10 (here: 10) as additional widening thresholds. We should consider this also in our autotuner. They did not mention a specific example, but here is a simple example: int x = 0; while (nondet()) { x = (x + 1) % 10; } assert(x <= 10); In the example above, narrowing recovers precision, but here it wouldn't: int x = 1; while (nondet()) { if (x % 10 == 0) x = 1; else x++; } assert(x <= 10); In more realistic examples, this would probably be related to something such as a ring buffer maintaining the last k entries. Given this is only one additional line of code, I think it's worth trying.

precisiongood first issue
5
stan-dev/stanc3162 Tier A · 62 pts
#1677Opened 1 month ago
Peephole optimization for commonly paired functions

See #1674 for an initial attempt. Thanks to @sims1253 for the suggestion There are several functions that historically duplicated work and now can be done in a single call returning a tuple. These were primarily added in stan-dev/math#2931: qr: replaces calls to qr_Q/qr_R qr_thin: replaces calls to qr_thin_Q/qr_thin_R eigendecompose_sym: replaces calls to eigenvalues_sym/eigenvectors_sym eigendecompose: replaces calls to eigenvalues/eigenvectors complex_schur_decompose: replaces calls to complex_schur_decompose_t/complex_schur_decompose_u svd: replaces calls to svd_U/svd_V csr_extract : replaces calls to csr_extract_u/csr_extract_v/csr_extract_w The proposal is to add a small optimization which detects cases where these are called one after the other and rewrite them to use the combined version. It may also be useful to separately flag a warning for models where the older functions are both called on the same thing.

help wantedfeatureperformanceoptimization
1
Wyzer-Lang/wyzer250 Tier B · 52 pts
#11Opened 2 months ago
[Discussion] Ideal error handling.

A comment on Hacker News I agree with the OP here. Rather than blindly following Rust, I strongly believe that having a unique error handling system that goes well with the ideologies of wyzer will make it a more fun language to work with.

enhancementgood first issue
8
goblint/analyzer252 Tier A · 66 pts
#2093Opened 2 months ago
`sem.malloc.fail` makes memory leak analysis less precise

While reviewing https://gitlab.com/sosy-lab/benchmarking/sv-benchmarks/-/merge_requests/1686, I did runs with sem.malloc.fail enabled and found differences in memory leak analysis. For example, the result for sv-benchmarks/c/ldv-memsafety/memleaks_test1-3.i changes. In particular, it seems that calling free on a pointer, which can be known or NULL is less precise than just with the known pointer in the memLeak analysis. However, that shouldn't be the case: free(NULL) is fine and a no-op. This also seems to affect at least 1175 tasks in sv-benchmarks/Juliet_Test/.

sv-compprecisiongood first issue
goblint/analyzer252 Tier A · 66 pts
#2092Opened 2 months ago
`sem.malloc.fail` affects `alloca`

While reviewing https://gitlab.com/sosy-lab/benchmarking/sv-benchmarks/-/merge_requests/1686, I did runs with sem.malloc.fail enabled and found differences in tasks using alloca. According to the man page, alloca can never return NULL: RETURN VALUE The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behavior is undefined.

sv-compprecisiongood first issue
gwaithimirdain/narya306 Tier B · 48 pts
#130Opened 2 months ago
Split should handle window modalities

Currently I believe C-c C-y (split) doesn't work if a window modality would be required. Ideally it should just work if the term is a modally annotated free variable, since the window can be guessed to be its annotation, and should also allow the user to input a term behind a modal annotation to specify the window.

enhancementgood first issue
davesnx/styled-ppx415 Tier B · 45 pts
#588Opened 2 months ago
Umbrella: missing-property burn-down (185 standard properties)

What to build The generated coverage report (packages/css-grammar/data/coverage.md, make css-oracle) lists 185 missing standard properties, 47 legacy aliases, and 80 missing functions, each with its spec id and value-definition syntax. This is the umbrella issue for burning that list down. Work through it by spec module (e.g. css-borders-4, css-overflow-4, css-rhythm-1), not property-by-property, so shared value types land once. A contributor guide describing the end-to-end recipe for adding a property (spec dialect, runtime witnesses, testing, oracle regeneration) lives at documents/adding-css-properties.md. Important caveats: The syntax strings in the report come from the specs and are not paste-ready: styled-ppx's spec dialect diverges (interpolation alternatives, runtime witnesses in the hand-maintained type modules). Auto-generation of specs has been evaluated and rejected. Every addition must update the coverage report (make css-oracle) — CI gates on staleness. Acceptance cri

help wantedcss-support
goblint/analyzer252 Tier A · 66 pts
#2074Opened 3 months ago
Race Reporting: Employ Dominance Elimination

Following some discussions at Gobcon, it seems that dominance elimination in the sense of Paznikov et al. (https://arxiv.org/abs/2512.05555, and also Flanagan & Freund and Choi et al. earlier) may also be useful for taming the number of race reports. It also seems that if one follows their definition, the property of being true/false alarms is preserved: If r1 dominates r2, r2 is a true race iff r1 is a true race.

usabilitygood first issueexplainability
gwaithimirdain/narya306 Tier B · 48 pts
#126Opened 3 months ago
Locally defined indexed datatypes

The code that inspects the declared output type of a datatype constructor and extracts its indices assumes that its head is a constant. This means it is impossible to use explicit-output syntax in a local datatype defined with let rec, and hence also to define local indexed datatypes, since in that case the head is a metavariable. This should be a fairly simple fix, in Check.check_data add a clause testing equality of two metavariables with their environments alongside the one that tests equality of two constants.

enhancementgood first issue
#1281Opened 3 months ago
[FEAT] Support textDocument/selectionRange

Documented here: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange Demo of this feature using Rust's LSP: Screen.Recording.2026-07-08.at.16.28.52.mov A very simple first version could be to expand a selection to the entire sentence. This is exactly what rocq-lsp does. This would be very easy to implement. In the future, the implementation could be extended to increase selections based on the AST structure (especially in Gallina).

enhancementgood first issue
1
ocaml/merlin1,710 Tier A · 63 pts
#2080Opened 3 months ago
truncated error message with Emacs+Merlin on OCaml 5.04 and v5.7.1-504

Repro case: module type A = sig type a end module type B = sig type b end module A : A = struct type a end module B : B = struct type b end module F (_ : A) (_ : B) = struct end module Test = F(B)(A) Merlin shows the following error message: 2. Modules do not match: A : sig type a = A.a end is not included in B (The exact same thing is in the *Messages* and *merlin-errors* buffer.) This is only the last line of the full error message shown by the compiler: Error: This application of the functor F is ill-typed. These arguments: B A do not match these parameters: (Arg : A) (Arg : B) -> ... 1. Modules do not match: B : sig type b = B.b end is not included in A The type a is required but not provided File "test.ml", line 1, characters 20-26: Expected declaration 2. Modules do not match: A : sig type a = A.a end is not included in B

Area/Emacsgood first issue
7
ocaml-gospel/gospel165 Tier B · 43 pts
#477Opened 4 months ago
Remove `string` from Gospel type

The string type is still defined in the Structure module as a built in gospel type but it is not used as model for any OCaml type. Indeed, the model for OCaml strings is char sequence (which makes perfect sense). This can be a bit confusing for an unaware user as one will have some type checking errors when using Gospel strings to talk about OCaml ones. For instance, this piece of specification, though quite natural, won't type check, accepting the person type declaration but failing on the ensures clause: (*@ type person = { firstname: string; lastname: string } *) type t (*@ model : person *) val make : string -> string -> t (*@ t = make firstname lastname ensures t = { firstname; lastname } *) A possible bonus would be to parse "example" as a char sequence.

good first issue
3
panglesd/slipshow898 Tier A · 56 pts
#237Opened 4 months ago
allow frontmatter delimiters with trailing whitespace

I had copy-pasted the theme setting that goes at the top from a pager which left a bunch of whitespace I hadn't seen. ---__________ theme: vanier ---__________ I was puzzled as to why it would display verbatim in the slides. I later worked out what the issue was. It might be useful to strip the whitespace after the --- or at least warn the user abuot it.

good first issue
1
#70Opened 5 months ago
Self-assignment detection

Implement the dead simple rule that catches x := x, because apparently some implementations of ST allow this.

enhancementgood first issuenew detector
2
goblint/analyzer252 Tier A · 66 pts
#1986Opened 6 months ago
Detect calls to functions that are not `signal-safe` in signal handlers

For reviewing #1965, I read some documentation on signal handlers. There is in particular this rule An async-signal-safe function is one that can be safely called from within a signal handler. Many functions are not async-signal-safe. In particular, nonreentrant functions are generally unsafe to call from a signal handler. https://man7.org/linux/man-pages/man7/signal-safety.7.html and a list of such async-signal-safe functions. We may want to add an analysis that tracks whether the current thread is a signal handler, and warn whenever a library function that is not async-signal-safe is called. May be a good BSc project.

student-jobgood first issue
1
ocaml/odoc356 Tier A · 63 pts
#1409Opened 6 months ago
[Markdown] Line tags as frontmatter fields

I think it can be beneficial for the markdown backend to support line tags (https://ocaml.github.io/odoc/odoc/odoc_for_authors.html#line-tags), and it currently fits the design of frontmatter data. I wonder whether we should make it a setting or set it as the default. What would you say? From jonludlam/parseff@7d0cd4e#diff-346adc8f62cbd27737f177027dba7b4a9bb7d64d9c48f6738624a88281949e36R1-R2 @short_title Quick start @children_order primitives combinators repetition convenience errors diagnostics zero-copy streaming binary utf8 guides/ parseff/ src/ --- short_title: "Quick start" children_order: ["primitives", "combinators", "repetition", "convenience", "errors", "diagnostics", "zero-copy", "streaming", "binary", "utf8", "guides/", "parseff/", "src/"] --- # Article Content Begins Here Something worth pointing out is that "children_order" won't be used in the markdown case, and the list is viable fields is limited, wonder if this is by design and there's a solid reason behind.

good-first-issue
ocaml/odoc356 Tier A · 63 ptsPR #1496 In Progress
#1405Opened 6 months ago
Display `[@atomic]` annotations in record fields

Since OCaml 5.4, one can annotate a mutable record field to have atomic getters and setters. This information should be reflected in odoc's display of the field!

good-first-issue
ocaml/ocaml6,601 Tier A · 62 ptsPR #14756 In Progress
#14658Opened 7 months ago
feature request: `OCAMLRUNPARAM=s=2MB`

Some of the OCAMLRUNPARAM parameters (see documentation in the manual) denote memory space, and their units are typically in words. For example s=2M requests 2^20 words of minor heap size, that is 16 Mibibytes (MiB) on 64bits machines. I find this very confusing, and I always read those as MiB. I propose to add support for units w (words) and B (bytes), where bytes are translated back to words following the current machine word size. This would let users (optionally) be explicit and clear in their setting, s=2MB or s=2Mw. Note: I considered asking for using 2MiB as well, to be explicit that those are power-of-two units, but this introduces a risk of confusion as users might then assume that 2MB is in base ten (uncommon in computer science... except for disk space).

newcomer-jobfeature-wishgood first issue
10
multikernel/kernelscript508 Tier B · 48 pts
#15Opened 7 months ago
Support ebpf program live update
help wanted
4
AeneasVerif/aeneas1,031 Tier A · 66 pts
#822Opened 7 months ago
Support for early returns inside loops that can be replaced with breaks

Hi team, as discussed in #general > Guidance on translating recursive code @ 💬 Zulip it should be possible to support early returns inside loops if they are equivalent to breaks. For instance (using Aeneas and Charon built from 39e3086), charon cargo --preset=aeneas runs successfully for the code below: fn my_fn(x: i32) -> Option<i32> { if x >= 0 { let mut y = x; loop { if y == 0 { return Some(y); } if y % 2 == 1 { y /= 2; } else { break; } } } None } fn main() { println!("{:?}", my_fn(42)); } And fails with aeneas -backend lean return_inside_loop.llbc with error: [�[92mInfo�[39m ] Imported: return_inside_loop.llbc �[?25lApplied prepasses: [----------------------------------------------------] 0/9 ⠋ Applied prepasses: [#####-----------------------------------------------] 1/9 ⠋ Applied prepasses: [#########################

buggood first issuetranslationprepasses
2
ocaml-ppx/ppxlib301 Tier B · 51 pts
#626Opened 8 months ago
Better migration errors

This might be a thing of the past if we continue our encoding/decoding trajectory, however, if we do have a migration error I think we can do much better in terms of what the error actually says. For example, for many users it is not obvious that this even has anything to do with a ppx let along ppxlib! See: https://mathstodon.xyz/@jonmsterling/116085163003251340 I think we should mention more things and potentially point to ocaml-ppx/ppxlib/issues :)

enhancementgood first issue
2
ocaml/merlin1,710 Tier A · 63 pts
#2033Opened 8 months ago
Unable to find declaration of type when editor position is in another declaration

See ocaml/ocaml-lsp#1586

Kind/Buggood first issue
1
ocaml/dune1,922 Tier S · 69 pts
#13525Opened 8 months ago
Formatting dune files on save while the build is running

Desired Behavior Imagine you have an editor extension enabled that can format your dune files (dune, dune-project, etc.) on save, using dune format-dune-file under the hood. I think now if you have a build running in watch mode in the project, this no longer works. The format command conflicts with the build locks. I think this use to work before dune.3.21 but I suspect something changed. Repro? I'd appreciate if someone else can confirm as I am not sure exactly sure from where this workflow comes from (dune.el?). I have noticed it via some other layer with the reformatter emacs package (using this setup). Not sure if this applies to other users. Thanks!

good first issuerpc
2

Top Ocaml Repositories with Beginner Issues (14)

Updated Daily via GitHub GraphQL
AeneasVerif
aeneas
AeneasVerif/aeneas
A•Welcoming
1.0k·OCOCaml

A verification toolchain for Rust programs

#compiler#coq#deductive-reasoning
63%
Merge Rate
-
Review Time
56%
1st-Timers
1 GFI
Scorecard
goblint
analyzer
goblint/analyzer
A•Welcoming
252·OCOCaml

Static analysis framework for C

#abstract-interpretation#c#ocaml
65%
Merge Rate
-
Review Time
67%
1st-Timers
3 GFI
Scorecard
ocaml
odoc
ocaml/odoc
A•Welcoming
356·OCOCaml

Documentation compiler for OCaml

64%
Merge Rate
-
Review Time
75%
1st-Timers
2 GFI
Scorecard
ocaml
merlin
ocaml/merlin
A•Welcoming
1.7k·OCOCaml

Context sensitive completion for OCaml in Vim and Emacs

#atom#emacs#ocaml
68%
Merge Rate
-
Review Time
80%
1st-Timers
2 GFI
Scorecard
stan-dev
stanc3
stan-dev/stanc3
A•Welcoming
162·OCOCaml

The Stan transpiler (from Stan to C++ and beyond).

74%
Merge Rate
-
Review Time
50%
1st-Timers
2 GFI
Scorecard
ocaml
ocaml
ocaml/ocaml
A•Welcoming
6.6k·OCOCaml

The core OCaml system: compilers, runtime system, base libraries

#compiler#functional-language#ocaml
57%
Merge Rate
-
Review Time
53%
1st-Timers
1 GFI
Scorecard
Wyzer-Lang
wyzer
Wyzer-Lang/wyzer
B•SolidLow Sample
250·OCOCaml

The Wyzer Programming Language

#compiler#language#wyzer
75%
Merge Rate
1h
Review Time
100%
1st-Timers
1 GFI
Scorecard
ocaml-ppx
ppxlib
ocaml-ppx/ppxlib
B•SolidLow Sample
301·OCOCaml

Base library and tools for ppx rewriters

80%
Merge Rate
-
Review Time
100%
1st-Timers
1 GFI
Scorecard
gwaithimirdain
narya
gwaithimirdain/narya
B•SolidLow Sample
306·OCOCaml

A proof assistant for higher-dimensional type theory

40%
Merge Rate
-
Review Time
0%
1st-Timers
2 GFI
Scorecard
multikernel
kernelscript
multikernel/kernelscript
B•SolidLow Sample
508·OCOCaml

KernelScript is a modern, type-safe, domain-specific programming language for eBPF-centric kernel customization

#ebpf#linux-kernel#multikernel
100%
Merge Rate
-
Review Time
100%
1st-Timers
1 GFI
Scorecard
davesnx
styled-ppx
davesnx/styled-ppx
B•Solid
415·OCOCaml

styled components for Reason/OCaml with type-safe CSS and static extraction

#css#melange#ocaml
12%
Merge Rate
-
Review Time
100%
1st-Timers
1 GFI
Scorecard
iec-checker
iec-checker
iec-checker/iec-checker
B•SolidLow Sample
105·OCOCaml

Static analysis of IEC 61131-3 programs

#ics#iec61131-3#industrial-automation
71%
Merge Rate
-
Review Time
100%
1st-Timers
1 GFI
Scorecard
ocaml-gospel
gospel
ocaml-gospel/gospel
B•SolidLow Sample
165·OCOCaml

A tool-agnostic formal specification language for OCaml.

75%
Merge Rate
-
Review Time
0%
1st-Timers
1 GFI
Scorecard
semgrep
semgrep
semgrep/semgrep
D•RiskyLow Sample
16.9k·OCOCaml

Lightweight static analysis for many languages. Find bug variants with patterns that look like source code.

#c#go#java
-
Merge Rate
2d
Review Time
0%
1st-Timers
1 GFI
Scorecard

How to make your first Ocaml open-source pull request

Finding approachable Good First Issues in Ocaml allows you to build real-world software engineering experience. Instead of submitting PRs to abandoned repositories, GetMerged verifies maintainer review speeds and first-timer acceptance rates before you write a single line of code.

Select any issue above to claim it directly on GitHub, or click a repository to inspect full maintainer review turnaround metrics and triage guidance!

Get this week's top welcoming repos + fresh Good First Issues for Ocaml

Free weekly email, scoped to Ocaml. No account needed - confirm once and unsubscribe anytime.