# Confusion about code structuring, dune, ocamlformat, ocamldoc (overall workflow)

**URL:** <https://discuss.ocaml.org/t/confusion-about-code-structuring-dune-ocamlformat-ocamldoc-overall-workflow/17726>\
**Category:** Learning\
**Tags:** ocamlformat, ocamldoc, dune\
**Created:** [January 22, 2026, 10:24pm UTC](https://discuss.ocaml.org/t/confusion-about-code-structuring-dune-ocamlformat-ocamldoc-overall-workflow/17726 "2026-01-22T22:24:48Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://discuss.ocaml.org/u/jbe)\
**Post date:** [January 22, 2026, 10:24pm UTC](https://discuss.ocaml.org/t/confusion-about-code-structuring-dune-ocamlformat-ocamldoc-overall-workflow/17726/1 "2026-01-22T22:24:48Z")

</div>

I am still (somewhat) new to OCaml and I struggle with the overall workflow of creating a project, organizing my code, documenting it, and automatically creating documentation. I would like to share my experiences here and ask a few questions.

I created a project as follows (FreeBSD sh):

```plaintext
$ dune init project demo_project demo_project

```

I also created an empty `.ocamlformat` file at `demo_project/.ocamlformat`.

Then I created two files in `demo_project/lib/`, which were autoformatted (after running `dune fmt`) as follows:

### `demo_project/lib/demo_project.ml`:

```ocaml
(** Demo project *)

module type T = Foo.T
(** Some type *)

module M = Foo.M
(** Some module *)

```

### `demo_project/lib/foo.ml`:

```ocaml
(** Foo *)

(** Foo's module type T *)
module type T = sig
  val hello : unit -> unit
  (** Greeting *)
end

module type T2 = T
(** Foo's module type T2 *)

(** Foo's module M *)
module M = struct
  (** Greet with "Hello!" *)
  let hello () = print_endline "Hello!"
end

```

When I tried to create documentation of my code using `dune build @doc`, nothing happened:

```plaintext
jbe@localhost:~/demo_project/lib $ dune build @doc
Entering directory '/home/jbe/demo_project'
Leaving directory '/home/jbe/demo_project'

```

I searched on the net, and people said I need a `public_name` for my library, so I modified `demo_project/lib/dune` as follows:

```diff
 (library
- (name demo_project))
+ (name demo_project)
+ (public_name demo_project))

```

It took me some time to find out I need to run `dune build @doc` it in a different directory than I did (even though the output indicated that the program found my project root by itself). So the following worked:

```plaintext
jbe@localhost:~/demo_project/lib $ cd ..
jbe@localhost:~/demo_project $ dune build @doc
jbe@localhost:~/demo_project $ 

```

No output to `stdout`, but the documentation is created. I don’t understand why.

To add to my confusion, `dune build @doc-private` seems to build some docs, even if run inside the `lib/` directory. What is going on?

But as long as

- there is a `public_name` in my `lib/dune` file, **and**
- I run `dune build @doc` in the right directory

the documentation gets created now.

Another thing I am confused about is the position of the documentation comments (as placed by `dune fmt`):

- When does a doc comment appear before, and when after an item?
- Why is `val hello`’s doc comment placed _after_ and `let hello`’s doc comment placed _before_ the corresponding item?
- Why is `module type T`’s doc comment placed _before_ and `module type T2`’s doc comment placed _after_ the respective module type definition? Both are module types.

I wonder: Is it idiomatic to move some of your code in a private module (here `Foo`, for example) and then import/re-export specific items? I would say I’m following the documentation on [Library Wrapper Modules](https://ocaml.org/docs/libraries-dune#library-wrapper-modules) here, so I guess it is reasonable to do. This re-exporting also seems to be necessary in some cases when you want to strucuture your code in different files, because, for example, it’s not possible to place a functor directly in an own file.

Regading modules and re-exports, I think this also touches my yet-unanswered question from a different thread:

> [@Beginner's question about optimizing a float -\> float function with Owl](https://discuss.ocaml.org/t/beginners-question-about-optimizing-a-float-float-function-with-owl/17693/2):
>
> 1. I got a bit confused by `Owl_algodiff` vs `Owl.Algodiff`. What is the difference? I suspect `Owl_algodiff` is more the internal module and `Owl.Algodiff` is the official API? I noticed, for example, that we have `Owl.Arr` but no `Owl_arr`.

Should I have modules that I name `Public_name_foo` instead of my internal module `Foo`?

I’m sorry my questions are a bit vague. I guess what I want to know is: How do I typically organize my code into public and private modules?

Also, I ran into some issues and questions regarding ocamldoc in that matter:

- Where do I document my items if I re-export them by the library wrapper module? Note in my code there are comments where I re-export the `module type T` and `module M`, and I also have comments inside the `lib/foo.ml` file, where I define those. Apparently what ocamldoc does is inconsistent:
  - In Module `Demo_project`, I see the comments of the _re-exports_. When I follow the link to the module type `T`, I also see the comment from the re-export (`Some type`). But when I follow the link to the module `M`, I see _both_ the comment from the re-export (`Some module`) _and_ the comment where `M` was defined in `foo.ml` (`Foo's module M`).

- Why do modules and module types behave differently with regard to which documentation comments show up on a re-export?

Also, when I re-export the module `Foo.M` as `Foo`, the links disappear in the documentation:

```diff
 (** Demo project *)
 
 module type T = Foo.T
 (** Some type *)
 
-module M = Foo.M
+module Foo = Foo.M
 (** Some module *)

```

Now after this change, the module type `Demo_project.T` and the now renamed module `Demo_project.Foo` (internally `Foo.M`) both disappear from the documentation. Even though, I can still use them:

```ocaml
utop # Demo_project.Foo.hello ();;
Hello!
- : unit = ()

```

What is going on? Is this a bug of ocamldoc? Am I allowed to do this re-export or is it not really allowed?

Sorry this is a lot of questions and not as structured. Maybe someone can just point me to the right page with documentation. Or also feel free to ask back, if my questions aren’t clear.

---

<div class="post-metadata">

**Author:** ![jbe](https://avatars.discourse-cdn.com/v4/letter/j/f6c823/32.png) [@jbe](https://discuss.ocaml.org/u/jbe)\
**Post date:** [January 23, 2026, 10:47am UTC](https://discuss.ocaml.org/t/confusion-about-code-structuring-dune-ocamlformat-ocamldoc-overall-workflow/17726/2 "2026-01-23T10:47:01Z")

</div>

> [@jbe](#):
>
> But when I follow the link to the module `M`, I see _both_ the comment from the re-export (`Some module`) _and_ the comment where `M` was defined in `foo.ml` (`Foo's module M`).

Something seems buggy. I could reproduce another weird behavior as follows. Consider a new project initialized with `dune init project prj2 prj2`:

### `prj2/lib/dune`:

```plaintext
(library
 (name prj2)
 (public_name prj2))

```

### `prj2/lib/prj2.ml`:

```ocaml
include Submodule

```

### `prj2/lib/submodule.ml`:

```ocaml
(** Submodule *)

module M = struct end
(** Some comment on M *)

```

Running `dune build @doc` from the `prj2/` directory results in “`Some comment on M`” appearing twice in `prj2/_build/default/_doc/_html/prj2/Prj2/M/index.html`:

> Up – Index » prj2 » Prj2 » M
> 
> # Module `Prj2.M`
> 
> Some comment on M
> 
> Some comment on M

Is this a bug, or am I doing something wrong?

I use:

```plaintext
$ dune --version
3.20.2
$ ocamldoc --version
5.4.0

```
