QGIS Plugin Development Through Gary Sherman's Documentation
Gary Sherman built QGIS from scratch starting around 2002. Most of what you read about programming for QGIS traces back to documentation he wrote or shaped, especially the Programmer's Guide and various SDK reference materials scattered across the old wiki and later the official documentation site. If you're trying to write a QGIS plugin today, you're still reading material he originally authored, even if it's been revised by other contributors over the years. The current official Programmers Guide Gary Sherman is associated with lives at docs.qgis.org and covers the C++ and Python plugin APIs. It explains how to hook into QGIS core classes, build custom processing algorithms, and interface with the canvas and layer system. The guide isn't always updated in lockstep with every release, so expect some sections to drift.
Where to find the Programmers Guide Gary Sherman
The most reliable home for the guide is the official QGIS documentation at https://docs.qgis.org/latest/en/docs/pyqgis_developer_cookbook/. The older wiki versions, which Sherman contributed to heavily before migrating content, are archived but harder to navigate. If you want the canonical version, stick with docs.qgis.org. Look for the PyQGIS Developer Cookbook and the C++ API reference sections. The documentation breaks into several major sections: the plugin architecture overview, the Python plugin framework, the C++ plugin framework, the processing framework, and API reference material. The Python section is the one most people actually use. The C++ section matters if you're writing performance-critical code or extending QGIS itself rather than just building plugins. Here's something the guide doesn't make immediately obvious: the Python API and the C++ API are not perfectly mirrored. Functions exist in C++ that don't have Python bindings, and vice versa for some wrapper-level conveniences. If you hit a missing method in Python, the C++ API docs sometimes show the underlying implementation, which lets you work around it.
Setting Up a Development Environment
You don't need to compile QGIS from source to start. The Plugin Builder plugin inside QGIS does most of the heavy lifting. Install it from the plugin manager, create a new project, and it scaffolds a plugin structure with setup.py, metadata.txt, and a starter Python file. The first time you do this, the directory layout will seem confusing. The key paths are your plugin folder inside .qgis2 or .qgis3 depending on your setup, and the resources.qrc file if you're adding icons or forms. If you're doing C++ plugin development, you need a full QGIS source tree checked out, CMake, and a compiler that matches your platform. On Linux, this means your distro's dev packages for Qt5 and the QGIS headers. On Windows, it's significantly more painful. I spent about two days getting MSVC to cooperate with CMake when I was compiling against a custom QGIS build. Use MinGW or stick to Python unless you have a strong reason not to.
Get the Full Details

A Real Problem I Hit With the Guide
The Programmer's Guide shows you how to connect to the active layer signal using QgsMapLayerRegistry, but that class was deprecated in QGIS 3.x. I spent roughly an hour debugging a plugin that crashed on startup because the registry object was None. The old guides on the web still reference QgsMapLayerRegistry.instance().layers(), which simply doesn't work anymore. The fix is using QgsProject.instance().layers() instead. The guide has been partially updated but older tutorial pages online haven't caught up. If you're following any third-party article that mentions QgsMapLayerRegistry, double-check whether it's written for QGIS 2 or 3. One thing that catches people frequently is the difference between the plugin's initGui method and the run method. initGui runs when QGIS loads the plugin and sets up menu entries and toolbar buttons. run is what executes when the user clicks the button. If you put your main logic in initGui, it runs at startup before the canvas is ready, and you'll get null reference errors on layer operations. Another issue is coordinate reference systems. The guide explains CRS handling but doesn't emphasize strongly enough that getLayerById returns a layer in its native CRS, not necessarily in the project CRS. If you're doing geometry calculations without explicitly reprojecting, your distances and areas will be wrong. Call transformContext() and QgsCoordinateTransform() explicitly when you need accurate measurements across different CRS setups.
What the Guide Gets Wrong or Leaves Out
The documentation covers the happy path well. It does not cover packaging and distributing plugins effectively. The section on creating a release build is thin. If you want to publish on the official plugin repository, you need a specific zip structure, a proper metadata.xml, and version tagging that matches QGIS's expectations. The guide assumes you're developing locally and testing interactively. It doesn't walk through the CI workflow or how to handle transitive dependencies in your plugin package. There's also no real discussion of testing infrastructure. QGIS has a test framework based on pytest, but the guide barely mentions it. For any plugin that does more than basic UI work, you should be writing automated tests. Without them, you're guessing whether your changes break something downstream.
Python Plugin Structure That Actually Works
A working plugin needs these files: __init__.py with the classFactory function, metadata.txt with version and QGIS version compatibility, the main plugin module, and a resources file if you need Qt resources. The __init__.py classFactory function receives the iface object and returns your plugin class instance. That iface object is your gateway to the QGIS application — it gives you access to the canvas, the layer tree, the active layer, and the interface methods. Here's a minimal structure that avoids the most common errors: MyPlugin/__init__.py defines classFactory and provides metadata. MyPlugin/MyPlugin.py contains the plugin class with initGui and unload methods. MyPlugin/metadata.txt declares the required QGIS version range. If you skip any of these, the plugin manager will reject your plugin or fail to load it silently.

C++ vs Python: Which Path to Take
Python plugins are faster to develop and easier to debug. You can edit the source files and reload the plugin without recompiling. The overhead is acceptable for most use cases. C++ plugins are necessary when you're doing heavy computational work, integrating with native libraries, or building something that ships as part of QGIS itself rather than as a user-installed plugin. Performance-wise, a well-written Python plugin using vectorized operations through numpy or the built-in QGIS processing algorithms will often match a C++ equivalent for typical GIS workflows. The bottleneck is usually your algorithm logic, not the language choice. I've seen Python plugins process millions of points without issues when the code was structured correctly.
Debugging Plugins Effectively
Use print statements liberally at first. The Python console in QGIS captures stdout from plugins. Once your plugin gets complex, switch to the logging framework with QgsMessageLog.logMessage. It handles timestamps and categorization automatically. For crashes that happen outside Python, you need gdb or your platform's equivalent. On Linux, run QGIS from a terminal with QT_DEBUG_PLUGINS=1 to see loading errors. I once had a plugin that crashed only when a specific layer type was active. The crash report pointed to a null pointer in the canvas render loop, but the cause was my plugin registering a signal handler that fired during a canvas refresh it shouldn't have touched. The workaround was adding a guard clause that checked whether the canvas sender was valid before executing the handler logic. The guide doesn't cover signal lifecycle management in detail, so this kind of issue comes from experience rather than documentation.
The Processing Framework
One of the most powerful parts of QGIS that the guide explains reasonably well is the processing framework. You can create custom algorithms that appear in the Processing toolbox alongside built-in tools. This means your plugin's functionality becomes discoverable to other users who never install your plugin directly — they just use the algorithm through the Processing GUI or model builder. Writing a Processing algorithm in Python requires subclassing QgsProcessingAlgorithm and implementing the initAlgorithm, processAlgorithm, and helpText methods. The guide shows examples but the parameter system has quirks. QgsProcessingParameterFeatureSource and QgsProcessingParameterFeatureSink behave differently than you'd expect when used in batch mode versus interactive mode. Test both paths before shipping.

Version Compatibility Across QGIS Releases
QGIS 3 introduced breaking API changes that invalidated many QGIS 2 plugins. The Programmer's Guide covers QGIS 3 but some legacy content from the wiki era still circulates online. Always check the version header on any guide or tutorial you follow. If the URL contains /2.18/ or /lutraconsulting/, it's outdated. The current stable documentation at docs.qgis.org tracks the latest release but occasionally lags behind by a few weeks after a new version ships.