diff --git a/docs/src/creating-packages.md b/docs/src/creating-packages.md index 911cae8142..a20a453cc2 100644 --- a/docs/src/creating-packages.md +++ b/docs/src/creating-packages.md @@ -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`. @@ -393,7 +483,7 @@ This is done by making the following changes (using the example above): if !isdefined(Base, :get_extension) using Requires end - + @static if !isdefined(Base, :get_extension) function __init__() @require Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" include("../ext/PlottingContourExt.jl")