Существует несколько вариантов.
Вы можете встроить язык сценариев (или всю виртуальную машину, например .NET или JVM) в свое приложение, предоставляя достойный API для всех внутренних функций. Если ваше приложение уже построено на базе такой виртуальной машины, скорее всего, вам не нужно делать что-то конкретное для обеспечения расширяемости, просто убедитесь, что ваш API доступен и задокументирован. Популярные варианты встроенных сценариев: Lua, Python, Guile и Tcl.
В качестве альтернативы для чисто нативного кода вы можете предоставить свой API-интерфейс в виде отдельной библиотеки динамической компоновки и разрешить загрузку сторонних модулей (связанных с этой библиотекой).
Вы также можете сделать свое приложение модульным (разделенным на отдельные процессы), когда компоненты будут общаться друг с другом по простому текстовому протоколу через каналы или сокеты. Для такого варианта интеграции, который известен как «путь Unix», доступна очень сложная и мощная инфраструктура. В этом случае пользователи смогут выбрать любой способ интеграции своих расширений с вашими основными функциями.
Выберите любой, в зависимости от характера вашего приложения.