Skip to content

Commit

Permalink
Document how public API is defined (depends on scoped export) (#3511)
Browse files Browse the repository at this point in the history
  • Loading branch information
LilithHafner authored Sep 14, 2023
1 parent ff6d645 commit da8bae7
Showing 1 changed file with 90 additions and 0 deletions.
90 changes: 90 additions & 0 deletions docs/src/creating-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,96 @@ julia> HelloWorld.greet_alien()
Hello aT157rHV
```

## Defining a public API

If you want your package to be useful to other packages and you want folks to be able to
easily update to newer version of your package when they come out, it is important to
document what behavior will stay consistent across updates.

Unless you note otherwise, the public API of your package is defined as all the behavior you
describe about public symbols. A public symbol is a symbol that is exported from your
package with the `export` keyword or marked as public with the `public` keyword. When you
change the behavior of something that was previously public so that the new
version no longer conforms to the specifications provided in the old version, you should
adjust your package version number according to [Julia's variant on SemVer](#Version-specifier-format).
If you would like to include a symbol in your public API without exporting it into the
global namespace of folks who call `using YourPackage`, you should mark that symbol as
public with `public that_symbol`. Symbols marked as public with the `public` keyword are
just as public as those marked as public with the `export` keyword, but when folks call
`using YourPackage`, they will still have to qualify access to those symbols with
`YourPackage.that_symbol`.

Let's say we would like our `greet` function to be part of the public API, but not the
`greet_alien` function. We could the write the following and release it as version `1.0.0`.

```julia
module HelloWorld

export greet

import Random
import JSON

"Writes a friendly message."
greet() = print("Hello World!")

"Greet an alien by a randomly generated name."
greet_alien() = print("Hello ", Random.randstring(8))

end # module
```

Then, if we change `greet` to

```julia
"Writes a friendly message that is exactly three words long."
greet() = print("Hello Lovely World!")
```

We would release the new version as `1.1.0`. This is not breaking
because the new implementation conforms to the old documentation, but
it does add a new feature, that the message must be three words long.

Later, we may wish to change `greet_alien` to

```julia
"Greet an alien by a the name of \"Zork\"."
greet_alien() = print("Hello Zork")
```

And also export it by changing

```julia
export greet
```

to

```julia
export greet, greet_alien
```

We should release this new version as `1.2.0` because it adds a new feature
`greet_alien` to the public API. Even though `greet_alien` was documented before
and the new version does not conform to the old documentation, this is not breaking
because the old documentation was not attached to a symbol that was exported
at the time so that documentation does not apply across released versions.

However, if we now wish to change `greet` to

```julia
"Writes a friendly message that is exactly four words long."
greet() = print("Hello very lovely world")
```

we would need to release the new version as `2.0.0`. In version `1.1.0`, we specified that
the greeting would be three words long, and because `greet` was exported, that description
also applies to all future versions until the next breaking release. Because this new
version does not conform to the old specification, it must be tagged as a breaking change.

Please note that version numbers are free and unlimited. It is okay to use lots of them
(e.g. version `6.62.8`).

## Adding a build step to the package

The build step is executed the first time a package is installed or when explicitly invoked with `build`.
Expand Down

0 comments on commit da8bae7

Please sign in to comment.