Gleam Package Development Skill
This skill guides Claude Code through creating and publishing Gleam packages.
Primary Sources
- •Writing Gleam - Package structure and organization
- •gleam.toml Documentation - Package configuration
- •Hex.pm - Package repository
- •Gleam Package Interface - Package metadata
- •Awesome Gleam - Ecosystem reference
Creating a New Package
Initialize Package
gleam new my_package cd my_package
This creates the standard structure:
my_package/ ├── gleam.toml ├── manifest.toml ├── src/ │ └── my_package.gleam ├── test/ │ └── my_package_test.gleam └── README.md
See: Writing Gleam - Project Structure
Package Configuration
Edit gleam.toml:
name = "my_package"
version = "1.0.0"
description = "A clear, concise description of what the package does"
licences = ["Apache-2.0"]
repository = { type = "github", user = "username", repo = "my_package" }
[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
Package Design
API Design Principles
- •Make invalid states impossible - Use types to prevent errors
- •Qualified imports - Design for
package.function()usage - •Clear naming - Follow Gleam conventions
- •Minimal exports - Only export public API
- •Comprehensive documentation - Every public function
See: Coding Standards
Module Organization
For packages, organize by feature/domain:
src/ ├── my_package.gleam # Main API ├── my_package/ │ ├── internal.gleam # Internal helpers │ ├── parser.gleam # Parser functionality │ └── encoder.gleam # Encoder functionality
See: Conventions - Code Organization
Documentation
Document all public functions:
/// Parses a JSON string into a structured value.
///
/// ## Examples
///
/// ```gleam
/// parse("{\"name\": \"Alice\"}")
/// // -> Ok(Object([#("name", String("Alice"))]))
/// ```
///
/// ## Errors
///
/// Returns `Error(ParseError)` if the input is not valid JSON.
///
pub fn parse(input: String) -> Result(Json, ParseError) {
// Implementation
}
Library vs Application
Library Packages (MANDATORY)
- •NEVER panic or use
let assert - •Always return
Resultfor fallible operations - •Export only public API
- •Comprehensive test coverage
- •Clear documentation
See: Anti-patterns - Library Panicking
Application Packages
- •May panic at top level
- •Can use
let assertfor unrecoverable errors - •More relaxed about internal structure
Dependencies
Adding Dependencies
gleam add package_name gleam add --dev test_package
Dependency Constraints
Use semantic versioning constraints:
[dependencies] gleam_stdlib = ">= 0.34.0 and < 2.0.0" # Caret equivalent gleam_json = "~> 3.0" # Compatible with 3.x specific_version = "= 1.2.3" # Exact version
See: gleam.toml - Dependencies
Dependency Best Practices
- •Minimize dependencies
- •Use well-maintained packages
- •Keep versions up to date
- •Don't vendor dependencies in published packages
Cross-Platform Packages
Supporting Both Targets
Provide implementations for both Erlang and JavaScript:
@external(erlang, "crypto", "strong_rand_bytes") @external(javascript, "./crypto_ffi.mjs", "randomBytes") pub fn random_bytes(n: Int) -> BitArray
Or use Gleam fallback:
@external(erlang, "optimized_module", "fast_function")
pub fn my_function(input: String) -> Result(Output, Error) {
// Gleam implementation (runs on JavaScript)
slow_but_correct_implementation(input)
}
See: External Functions
Target-Specific Code
Use @target attribute when necessary:
@target(erlang)
pub fn erlang_only_function() -> Result(Value, Error) {
// Erlang-specific implementation
}
@target(javascript)
pub fn javascript_only_function() -> Result(Value, Error) {
// JavaScript-specific implementation
}
Testing
Test Coverage
- •Test all public functions
- •Test edge cases
- •Test error conditions
- •Test both targets if cross-platform
gleam test # Default target gleam test --target erlang # Erlang target gleam test --target javascript # JavaScript target
See: Testing Skill
Example-Based Tests
Include examples in documentation and test them:
/// ## Examples
///
/// ```gleam
/// add(2, 3)
/// // -> 5
/// ```
pub fn add(a: Int, b: Int) -> Int {
a + b
}
pub fn add_test() {
let assert 5 = add(2, 3)
}
Publishing to Hex.pm
Prerequisites
- •Create account on hex.pm
- •Authenticate:
gleam hex auth
Pre-publish Checklist
- • All tests passing
- • Documentation complete
- • README.md updated
- • CHANGELOG.md updated
- • Version bumped in gleam.toml
- • License file included
- • .gitignore configured
Publishing
gleam hex publish
See: Publishing Packages
Versioning
Follow Semantic Versioning:
- •MAJOR: Breaking API changes
- •MINOR: New features, backward compatible
- •PATCH: Bug fixes, backward compatible
Package Maintenance
Updating Dependencies
Check for outdated dependencies:
gleam deps outdated
Update dependencies:
gleam deps update
See: Gleam v1.10 Features
Deprecation
When deprecating functions, document clearly:
/// **Deprecated**: Use `new_function` instead.
///
/// This function will be removed in version 2.0.0.
@deprecated("Use new_function instead")
pub fn old_function() -> Result(Value, Error) {
new_function()
}
Breaking Changes
When making breaking changes:
- •Bump MAJOR version
- •Document changes in CHANGELOG
- •Provide migration guide
- •Consider deprecation period first
Package Quality
README Template
# Package Name
Brief description of what the package does.
## Installation
\`\`\`sh
gleam add package_name
\`\`\`
## Usage
\`\`\`gleam
import package_name
pub fn main() {
package_name.do_something()
}
\`\`\`
## Documentation
[View on HexDocs](https://hexdocs.pm/package_name/)
## Development
\`\`\`sh
gleam test
\`\`\`
## License
[License Name]
CI/CD for Packages
Example GitHub Actions:
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-beam@v1
with:
otp-version: "27.0"
gleam-version: "1.10.0"
- run: gleam deps download
- run: gleam test --target erlang
- run: gleam test --target javascript
- run: gleam format --check src test
Ecosystem Integration
Listing on Awesome Gleam
Submit PR to Awesome Gleam to increase visibility.
Package Categories
Common package categories:
- •Web frameworks
- •Database clients
- •Data formats (JSON, XML, CSV, TOML)
- •Cryptography
- •Testing utilities
- •CLI tools
- •Utilities
See: Awesome Gleam
Common Package Patterns
Builder Pattern
pub type Config {
Config(host: String, port: Int, timeout: Int)
}
pub fn new() -> Config {
Config(host: "localhost", port: 8080, timeout: 5000)
}
pub fn host(config: Config, host: String) -> Config {
Config(..config, host: host)
}
pub fn port(config: Config, port: Int) -> Config {
Config(..config, port: port)
}
See: Patterns - Builder
Result Helpers
pub fn try_map(
result: Result(a, e),
f: fn(a) -> Result(b, e)
) -> Result(b, e) {
case result {
Ok(value) -> f(value)
Error(error) -> Error(error)
}
}
Remember: A well-designed package is easy to use correctly and hard to use incorrectly. Focus on clear APIs and excellent documentation.