Short answer

Make GeoPackage your default working and delivery format, and treat shapefile as a legacy export you produce only when a recipient demands it. The shapefile's design dates to the early 1990s and carries hard limits that silently corrupt modern data: field names truncate to 10 characters, each .dbf/.shp/.shx component is capped near 2 GB, character encoding is unreliable, there are no true NULL values, and a "file" is actually three to seven sidecar files that must travel together. GeoPackage is a single OGC-standard SQLite database that removes every one of those constraints and can hold many vector layers, raster tiles, and metadata in one portable file.

Why shapefile breaks modern data

The shapefile specification is well documented and superbly supported, which is exactly why its defects are so dangerous — the data opens, looks fine, and is quietly wrong:

  • 10-character field names. groundwater_level becomes groundwate, and if two fields collide on the first 10 characters, the second is renamed groundwa_1. Joins keyed on attribute names then fail. This happens during every export to shapefile, not just on edge cases.
  • The 2 GB component limit. Each individual file (notably the .dbf attribute table and the .shp geometry) cannot exceed about 2 GB. Large LiDAR-derived vectors or dense national datasets hit this wall and truncate.
  • Encoding roulette. The .dbf is a dBASE table with weak encoding declaration. Accented characters and non-Latin scripts often arrive as mojibake unless the producer and consumer agree on a code page out of band (the .cpg sidecar helps but is frequently missing).
  • No true NULL. Shapefile cannot store a real NULL; empty numeric fields become 0, which is a different statement. A missing measurement and a measured zero become indistinguishable.
  • Field type and width limits. Numeric precision and field width are constrained, and date/time handling is coarse.
  • Multi-file fragility. Email or transfer the .shp alone and the dataset is dead — the geometry index and attributes live in the siblings.

What GeoPackage gives you instead

GeoPackage (.gpkg) is an OGC standard built on SQLite. From the GIS user's perspective:

  • One file, many layers. Vector layers, raster tile pyramids, and plain attribute tables coexist in a single container — ideal for handing a whole project to a colleague.
  • Full field names and real types. Long, unambiguous column names; proper INTEGER/REAL/TEXT/DATE types; genuine NULLs.
  • UTF-8 throughout. Unicode is the norm, so accents and non-Latin scripts survive.
  • Large datasets. Effectively bounded by SQLite limits (terabyte-scale), not 2 GB per component.
  • Spatial indexing. R-tree indexes give fast spatial queries; you can even open the file and run SQL directly.
  • Standards-based and tool-supported. Read/written by QGIS, GDAL/OGR, ArcGIS Pro, PostGIS tooling, and most modern stacks.

When to keep shapefile

Shapefile is still the most universally readable vector format on Earth, so:

  • A client's GIS, regulator portal, or legacy tool may only accept .shp. Deliver it, but keep your master in GeoPackage.
  • Tiny, simple, throwaway datasets where none of the limits bite.

Otherwise, there is no strong reason to work in shapefile in 2026.

Worked example: clean conversion with ogr2ogr

The risky moment is the conversion itself, because encoding and field names can change. Do it explicitly:

# Tell GDAL how the source .dbf is encoded, then write UTF-8 GeoPackage
SHAPE_ENCODING="ISO-8859-1" ogr2ogr -f GPKG sites.gpkg sites.shp \
  -nln boreholes \
  -lco ENCODING=UTF-8 \
  -nlt PROMOTE_TO_MULTI
  • SHAPE_ENCODING (a GDAL config option) declares the input code page so accents decode correctly. Get this wrong and you bake mojibake into the GeoPackage.
  • -nln boreholes names the destination layer meaningfully instead of inheriting the filename.
  • -nlt PROMOTE_TO_MULTI avoids the common "mixed single/multi part" failure by promoting to MultiPolygon/MultiLineString.

Bundle several shapefiles into one GeoPackage by appending:

ogr2ogr -f GPKG project.gpkg faults.shp -nln faults
ogr2ogr -f GPKG project.gpkg -update samples.shp -nln samples
ogr2ogr -f GPKG project.gpkg -update geology.shp -nln geology

Then verify:

ogrinfo -so project.gpkg faults     # check CRS, field names, feature count
ogrinfo -dialect SQLite -sql \
  "SELECT count(*) FROM faults WHERE ST_IsValid(geom)=0" project.gpkg

The validity check catches self-intersections and ring errors before they propagate downstream.

GeoPackage vs GeoJSON: a different axis

These solve different problems. GeoJSON (RFC 7946) is a text format for web exchange and, by the spec, uses WGS84 longitude-latitude (EPSG:4326) only. It is human-readable, perfect for small payloads and APIs, but bloated and slow for large analytical datasets and it cannot embed rasters or a spatial index. GeoPackage is the binary, indexed, multi-layer workhorse; GeoJSON is the lightweight wire format. Use GeoJSON to move a few hundred features into a web map; use GeoPackage to hold a project.

Common pitfalls and why they happen

  • Sending only the .shp. People treat the shapefile as one file; it is not, and the recipient gets unreadable geometry with no attributes.
  • Silent name truncation. A clean attribute schema collapses to 10-char stubs on export, breaking documented joins. Convert to GeoPackage before designing joins.
  • Encoding loss on conversion. Skipping SHAPE_ENCODING corrupts accented text. Always declare the source code page.
  • Assuming export success means clean data. A successful write says nothing about geometry validity or CRS correctness. Run ogrinfo and ST_IsValid afterward.
  • Zeros that should be NULL. Data round-tripped through shapefile turns missing values into 0; if you compute means or counts on it, the statistics are wrong.

QA checklist

  • Confirm CRS is set and correct in the GeoPackage (ogrinfo -so shows the SRS).
  • Verify field names survived intact and types are appropriate (real NULLs where data is missing).
  • Run a geometry validity check; fix invalid features with ST_MakeValid or QGIS Fix geometries.
  • Include a small metadata note (source, CRS, schema, date, allowed use) in the package or alongside it.
  • If a client needs shapefile, deliver it as a derived export and keep the GeoPackage as the master.

Bathyl perspective

We hold project data as GeoPackage so a single file carries every layer, its CRS, real NULLs, and full attribute names into the next person's session unchanged. Shapefile still ships when a recipient requires it, but it is always a downstream export from a clean master, never the place the data actually lives. A dataset is finished when another team can open, query, and trust it without a chain of out-of-band caveats.

Related reading

Sources